From 471c15d4be68f33938cea0ca4b52fd16c085d361 Mon Sep 17 00:00:00 2001 From: Leonid Stryzhevskyi Date: Mon, 3 Jun 2024 23:24:52 +0300 Subject: [PATCH 1/4] ErroHandling: implement simple API --- src/CMakeLists.txt | 2 + src/oatpp/web/protocol/CommunicationError.hpp | 2 +- src/oatpp/web/protocol/http/Http.hpp | 33 +- .../web/server/AsyncHttpConnectionHandler.cpp | 2 +- .../web/server/HttpConnectionHandler.cpp | 2 +- src/oatpp/web/server/HttpProcessor.cpp | 79 +- src/oatpp/web/server/HttpRequestHandler.hpp | 4 +- src/oatpp/web/server/HttpServerError.cpp | 38 + src/oatpp/web/server/HttpServerError.hpp | 57 ++ src/oatpp/web/server/api/ApiController.cpp | 7 +- src/oatpp/web/server/api/ApiController.hpp | 27 +- src/oatpp/web/server/handler/ErrorHandler.cpp | 82 +- src/oatpp/web/server/handler/ErrorHandler.hpp | 58 +- test/oatpp/AllTestsMain.cpp | 332 +++---- test/oatpp/web/FullAsyncTest.cpp | 278 +++--- test/oatpp/web/FullTest.cpp | 832 +++++++++--------- .../web/app/ControllerWithErrorHandler.hpp | 4 - 17 files changed, 966 insertions(+), 873 deletions(-) create mode 100644 src/oatpp/web/server/HttpServerError.cpp create mode 100644 src/oatpp/web/server/HttpServerError.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ce57de8f..bf98475f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -289,6 +289,8 @@ add_library(oatpp oatpp/web/server/HttpRequestHandler.hpp oatpp/web/server/HttpRouter.cpp oatpp/web/server/HttpRouter.hpp + oatpp/web/server/HttpServerError.cpp + oatpp/web/server/HttpServerError.hpp oatpp/web/server/api/ApiController.cpp oatpp/web/server/api/ApiController.hpp oatpp/web/server/api/Endpoint.cpp diff --git a/src/oatpp/web/protocol/CommunicationError.hpp b/src/oatpp/web/protocol/CommunicationError.hpp index 0aaa613f..e128c6da 100644 --- a/src/oatpp/web/protocol/CommunicationError.hpp +++ b/src/oatpp/web/protocol/CommunicationError.hpp @@ -123,7 +123,7 @@ public: * Get error info. * @return - error info. */ - Info getInfo() { + Info getInfo() const { return m_info; } diff --git a/src/oatpp/web/protocol/http/Http.hpp b/src/oatpp/web/protocol/http/Http.hpp index 3494174c..a37e4c58 100644 --- a/src/oatpp/web/protocol/http/Http.hpp +++ b/src/oatpp/web/protocol/http/Http.hpp @@ -419,35 +419,32 @@ public: /** * Constructor. - * @param info - * @param message + * @param info - Status object + * @param message - Error message + * @param headers - additional headers recommended for error response. */ - HttpError(const Info& info, const oatpp::String& message) + HttpError(const Info& info, + const oatpp::String& message, + const Headers& headers = {}) : protocol::ProtocolError(info, message) + , m_headers(headers) {} /** * Constructor. - * @param status - * @param message + * @param status - Status object + * @param message - Error message + * @param headers - additional headers recommended for error response. */ - HttpError(const Status& status, const oatpp::String& message) - : protocol::ProtocolError(Info(0, status), message) - {} - - /** - * Constructor. - * @param status - * @param message - * @param headers - */ - HttpError(const Status& status, const oatpp::String& message, const Headers& headers) + HttpError(const Status& status, + const oatpp::String& message, + const Headers& headers = {}) : protocol::ProtocolError(Info(0, status), message) , m_headers(headers) {} /** - * Get headers + * Get headers recommended for error response. * @return */ const Headers& getHeaders() const { @@ -463,7 +460,7 @@ public: * @param MESSAGE - String message. */ #define OATPP_ASSERT_HTTP(COND, STATUS, MESSAGE) \ -if(!(COND)) { throw oatpp::web::protocol::http::HttpError(STATUS, MESSAGE); } +if(!(COND)) { throw oatpp::web::protocol::http::HttpError(STATUS, MESSAGE, {}); } /** * Collection of HTTP Header constants. diff --git a/src/oatpp/web/server/AsyncHttpConnectionHandler.cpp b/src/oatpp/web/server/AsyncHttpConnectionHandler.cpp index 5f08705b..c04448c2 100644 --- a/src/oatpp/web/server/AsyncHttpConnectionHandler.cpp +++ b/src/oatpp/web/server/AsyncHttpConnectionHandler.cpp @@ -90,7 +90,7 @@ std::shared_ptr AsyncHttpConnectionHandler::createSh void AsyncHttpConnectionHandler::setErrorHandler(const std::shared_ptr& errorHandler){ m_components->errorHandler = errorHandler; if(!m_components->errorHandler) { - m_components->errorHandler = handler::DefaultErrorHandler::createShared(); + m_components->errorHandler = std::make_shared(); } } diff --git a/src/oatpp/web/server/HttpConnectionHandler.cpp b/src/oatpp/web/server/HttpConnectionHandler.cpp index 42b1717e..3f4f19d9 100644 --- a/src/oatpp/web/server/HttpConnectionHandler.cpp +++ b/src/oatpp/web/server/HttpConnectionHandler.cpp @@ -78,7 +78,7 @@ std::shared_ptr HttpConnectionHandler::createShared(const void HttpConnectionHandler::setErrorHandler(const std::shared_ptr& errorHandler){ m_components->errorHandler = errorHandler; if(!m_components->errorHandler) { - m_components->errorHandler = handler::DefaultErrorHandler::createShared(); + m_components->errorHandler = std::make_shared(); } } diff --git a/src/oatpp/web/server/HttpProcessor.cpp b/src/oatpp/web/server/HttpProcessor.cpp index 38ed67b9..e668651d 100644 --- a/src/oatpp/web/server/HttpProcessor.cpp +++ b/src/oatpp/web/server/HttpProcessor.cpp @@ -24,6 +24,7 @@ #include "HttpProcessor.hpp" +#include "oatpp/web/server/HttpServerError.hpp" #include "oatpp/web/protocol/http/incoming/SimpleBodyDecoder.hpp" #include "oatpp/data/stream/BufferStream.hpp" @@ -52,7 +53,7 @@ HttpProcessor::Components::Components(const std::shared_ptr& pRouter : Components(pRouter, nullptr, std::make_shared(), - handler::DefaultErrorHandler::createShared(), + std::make_shared(), {}, {}, std::make_shared()) @@ -62,7 +63,7 @@ HttpProcessor::Components::Components(const std::shared_ptr& pRouter : Components(pRouter, nullptr, std::make_shared(), - handler::DefaultErrorHandler::createShared(), + std::make_shared(), {}, {}, pConfig) @@ -89,33 +90,33 @@ HttpProcessor::processNextRequest(ProcessingResources& resources, std::shared_ptr response; - try{ + try { + try { - for(auto& interceptor : resources.components->requestInterceptors) { - response = interceptor->intercept(request); - if(response) { - return response; + for (auto &interceptor: resources.components->requestInterceptors) { + response = interceptor->intercept(request); + if (response) { + return response; + } } + + auto route = resources.components->router->getRoute(request->getStartingLine().method, + request->getStartingLine().path); + + if (!route) { + data::stream::BufferOutputStream ss; + ss << "No mapping for HTTP-method: '" << request->getStartingLine().method.toString() + << "', URL: '" << request->getStartingLine().path.toString() << "'"; + + throw oatpp::web::protocol::http::HttpError(protocol::http::Status::CODE_404, ss.toString()); + } + + request->setPathVariables(route.getMatchMap()); + return route.getEndpoint()->handle(request); + + } catch (...) { + std::throw_with_nested(HttpServerError(request, "Error processing request")); } - - auto route = resources.components->router->getRoute(request->getStartingLine().method, request->getStartingLine().path); - - if(!route) { - - data::stream::BufferOutputStream ss; - ss << "No mapping for HTTP-method: '" << request->getStartingLine().method.toString() - << "', URL: '" << request->getStartingLine().path.toString() << "'"; - - connectionState = ConnectionState::CLOSING; - oatpp::web::protocol::http::HttpError error(protocol::http::Status::CODE_404, ss.toString()); - auto ptr = std::make_exception_ptr(error); - return resources.components->errorHandler->handleError(ptr); - - } - - request->setPathVariables(route.getMatchMap()); - return route.getEndpoint()->handle(request); - } catch (...) { response = resources.components->errorHandler->handleError(std::current_exception()); connectionState = ConnectionState::CLOSING; @@ -139,9 +140,9 @@ HttpProcessor::ConnectionState HttpProcessor::processNextRequest(ProcessingResou std::shared_ptr response; if(error.status.code != 0) { - oatpp::web::protocol::http::HttpError httpError(error.status, "Invalid Request Headers"); - auto eptr = std::make_exception_ptr(httpError); - response = resources.components->errorHandler->handleError(eptr); + HttpServerError httpError(nullptr, "Invalid Request Headers"); + auto ePtr = std::make_exception_ptr(httpError); + response = resources.components->errorHandler->handleError(ePtr); connectionState = ConnectionState::CLOSING; } else { @@ -154,17 +155,21 @@ HttpProcessor::ConnectionState HttpProcessor::processNextRequest(ProcessingResou response = processNextRequest(resources, request, connectionState); try { + try { - for (auto& interceptor : resources.components->responseInterceptors) { - response = interceptor->intercept(request, response); - if (!response) { - oatpp::web::protocol::http::HttpError httpError(protocol::http::Status::CODE_500, "Response Interceptor returned an Invalid Response - 'null'"); - auto eptr = std::make_exception_ptr(httpError); - response = resources.components->errorHandler->handleError(eptr); - connectionState = ConnectionState::CLOSING; + for (auto &interceptor: resources.components->responseInterceptors) { + response = interceptor->intercept(request, response); + if (!response) { + HttpServerError httpError(request, "Response Interceptor returned an Invalid Response - 'null'"); + auto ePtr = std::make_exception_ptr(httpError); + response = resources.components->errorHandler->handleError(ePtr); + connectionState = ConnectionState::CLOSING; + } } - } + } catch (...) { + std::throw_with_nested(HttpServerError(request, "Error processing request during response interception")); + } } catch (...) { response = resources.components->errorHandler->handleError(std::current_exception()); connectionState = ConnectionState::CLOSING; diff --git a/src/oatpp/web/server/HttpRequestHandler.hpp b/src/oatpp/web/server/HttpRequestHandler.hpp index c5b8d8ff..15646c40 100644 --- a/src/oatpp/web/server/HttpRequestHandler.hpp +++ b/src/oatpp/web/server/HttpRequestHandler.hpp @@ -87,7 +87,7 @@ public: */ virtual std::shared_ptr handle(const std::shared_ptr& request) { (void)request; - throw HttpError(Status::CODE_501, "Endpoint not implemented."); + throw HttpError(Status::CODE_501, "Endpoint not implemented.", {}); } /** @@ -99,7 +99,7 @@ public: virtual oatpp::async::CoroutineStarterForResult&> handleAsync(const std::shared_ptr& request) { (void)request; - throw HttpError(Status::CODE_501, "Asynchronous endpoint not implemented."); + throw HttpError(Status::CODE_501, "Asynchronous endpoint not implemented.", {}); } /** diff --git a/src/oatpp/web/server/HttpServerError.cpp b/src/oatpp/web/server/HttpServerError.cpp new file mode 100644 index 00000000..62e44977 --- /dev/null +++ b/src/oatpp/web/server/HttpServerError.cpp @@ -0,0 +1,38 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#include "HttpServerError.hpp" + +namespace oatpp { namespace web { namespace server { + +HttpServerError::HttpServerError(const std::shared_ptr& request, const std::string& message) + : std::runtime_error(message) + , m_request(request) +{} + +const std::shared_ptr& HttpServerError::getRequest() const { + return m_request; +} + +}}} diff --git a/src/oatpp/web/server/HttpServerError.hpp b/src/oatpp/web/server/HttpServerError.hpp new file mode 100644 index 00000000..1c6aee56 --- /dev/null +++ b/src/oatpp/web/server/HttpServerError.hpp @@ -0,0 +1,57 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#ifndef oatpp_web_server_HttpServerError_hpp +#define oatpp_web_server_HttpServerError_hpp + +#include "oatpp/web/protocol/http/incoming/Request.hpp" + +namespace oatpp { namespace web { namespace server { + +/** + * HttpServerError + */ +class HttpServerError : public std::runtime_error { +private: + std::shared_ptr m_request; +public: + + /** + * Constructor + * @param request + * @param message + */ + HttpServerError(const std::shared_ptr& request, const std::string& message); + + /** + * Get request + * @return + */ + const std::shared_ptr& getRequest() const; + +}; + +}}} + +#endif //oatpp_web_server_HttpServerError_hpp diff --git a/src/oatpp/web/server/api/ApiController.cpp b/src/oatpp/web/server/api/ApiController.cpp index 180d9fc2..17b615b1 100644 --- a/src/oatpp/web/server/api/ApiController.cpp +++ b/src/oatpp/web/server/api/ApiController.cpp @@ -51,7 +51,7 @@ std::shared_ptr ApiController::getEndpointHandler void ApiController::setErrorHandler(const std::shared_ptr& errorHandler){ m_errorHandler = errorHandler; if(!m_errorHandler) { - m_errorHandler = handler::DefaultErrorHandler::createShared(); + m_errorHandler = std::make_shared(); } } @@ -82,7 +82,7 @@ std::shared_ptr ApiController::handleDefaultAuthor return m_defaultAuthorizationHandler->handleAuthorization(authHeader); } // If Authorization is not setup on the server then it's 500 - throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "Authorization is not setup."); + throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "Authorization is not setup.", {}); } const std::shared_ptr& ApiController::getContentMappers() const { @@ -91,8 +91,7 @@ const std::shared_ptr& ApiController::getContentMappers() // Helper methods -std::shared_ptr ApiController::createResponse(const Status& status, - const oatpp::String& str) const { +std::shared_ptr ApiController::createResponse(const Status& status, const oatpp::String& str) const { return ResponseFactory::createResponse(status, str); } diff --git a/src/oatpp/web/server/api/ApiController.hpp b/src/oatpp/web/server/api/ApiController.hpp index 367c5242..7a62c7be 100644 --- a/src/oatpp/web/server/api/ApiController.hpp +++ b/src/oatpp/web/server/api/ApiController.hpp @@ -31,6 +31,7 @@ #include "oatpp/web/server/handler/AuthorizationHandler.hpp" #include "oatpp/web/server/handler/ErrorHandler.hpp" #include "oatpp/web/server/handler/AuthorizationHandler.hpp" +#include "oatpp/web/server/HttpServerError.hpp" #include "oatpp/web/protocol/http/incoming/Response.hpp" #include "oatpp/web/protocol/http/outgoing/Request.hpp" #include "oatpp/web/protocol/http/outgoing/ResponseFactory.hpp" @@ -238,8 +239,8 @@ protected: } async::Action handleError(async::Error* error) override { - auto eptr = std::make_exception_ptr(*error); - auto response = m_handler->m_controller->m_errorHandler->handleError(eptr); + auto ePtr = std::make_exception_ptr(*error); + auto response = m_handler->m_controller->m_errorHandler->handleError(ePtr); return this->_return(response); } @@ -265,20 +266,24 @@ protected: if(m_method == nullptr) { if(m_methodAsync == nullptr) { - throw protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Handler method is nullptr."); + throw HttpServerError(request, "[ApiController]: Error. Handler method is nullptr"); } - throw protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Non-async call to async endpoint."); + throw HttpServerError(request, "[ApiController]: Error. Non-async call to async endpoint"); } try { - return (m_controller->*m_method)(request); + try { + return (m_controller->*m_method)(request); + } catch (...) { + std::throw_with_nested(HttpServerError(request, "[ApiController]: Error processing request")); + } } catch (...) { - auto response = m_controller->handleError(std::current_exception()); - if(response != nullptr) { + auto ePtr = std::current_exception(); + auto response = m_controller->handleError(ePtr); + if (response != nullptr) { return response; } - - throw; + std::rethrow_exception(ePtr); } } @@ -288,9 +293,9 @@ protected: if(m_methodAsync == nullptr) { if(m_method == nullptr) { - throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Handler method is nullptr."); + throw HttpServerError(request, "[ApiController]: Error. Handler method is nullptr"); } - throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "[ApiController]: Error. Async call to non-async endpoint."); + throw HttpServerError(request, "[ApiController]: Error. Async call to non-async endpoint"); } if(m_controller->m_errorHandler) { diff --git a/src/oatpp/web/server/handler/ErrorHandler.cpp b/src/oatpp/web/server/handler/ErrorHandler.cpp index 2f8baa89..75778216 100644 --- a/src/oatpp/web/server/handler/ErrorHandler.cpp +++ b/src/oatpp/web/server/handler/ErrorHandler.cpp @@ -23,58 +23,71 @@ ***************************************************************************/ #include "./ErrorHandler.hpp" - +#include "oatpp/web/server/HttpServerError.hpp" #include "oatpp/web/protocol/http/outgoing/BufferBody.hpp" namespace oatpp { namespace web { namespace server { namespace handler { -std::shared_ptr ErrorHandler::handleError(const std::exception_ptr& exceptionPtr) { +void DefaultErrorHandler::unwrapErrorStack(HttpServerErrorStacktrace& stacktrace, const std::exception& e) { - std::shared_ptr response; + stacktrace.stack.emplace_front(e.what()); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - /* Default impl for backwards compatibility until the deprecated methods are removed */ - try { - if(exceptionPtr) { - std::rethrow_exception(exceptionPtr); + if (auto httpServerError = dynamic_cast(std::addressof(e))) { + stacktrace.request = httpServerError->getRequest(); + } else if (auto httpError = dynamic_cast(std::addressof(e))) { + for(auto& h : httpError->getHeaders().getAll_Unsafe()) { + stacktrace.headers.putIfNotExists(h.first.toString(), h.second.toString()); } - } catch (protocol::http::HttpError& httpError) { - response = handleError(httpError.getInfo().status, httpError.getMessage(), httpError.getHeaders()); - } catch (std::exception& error) { - response = handleError(protocol::http::Status::CODE_500, error.what()); - } catch (...) { - response = handleError(protocol::http::Status::CODE_500, "An unknown error has occurred"); + stacktrace.status = httpError->getInfo().status; } -#pragma GCC diagnostic pop - return response; + try { + std::rethrow_if_nested(e); + } catch (const std::exception& nestedException) { + unwrapErrorStack(stacktrace, nestedException); + } catch (...) { + } } -std::shared_ptr ErrorHandler::handleError(const protocol::http::Status& status, const oatpp::String& message) { - Headers headers; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - return handleError(status, message, headers); -#pragma GCC diagnostic pop +std::shared_ptr DefaultErrorHandler::handleError(const std::exception_ptr& error) { + + HttpServerErrorStacktrace stacktrace; + stacktrace.status = protocol::http::Status::CODE_500; + + try { + if(error) { + std::rethrow_exception(error); + } + } catch (const std::exception& e) { + unwrapErrorStack(stacktrace, e); + } catch (...) { + + } + + return renderError(stacktrace); + } -std::shared_ptr -DefaultErrorHandler::handleError(const oatpp::web::protocol::http::Status &status, const oatpp::String &message, const Headers& headers){ +std::shared_ptr DefaultErrorHandler::renderError(const HttpServerErrorStacktrace& stacktrace) { data::stream::BufferOutputStream stream; stream << "server=" << protocol::http::Header::Value::SERVER << "\n"; - stream << "code=" << status.code << "\n"; - stream << "description=" << status.description << "\n"; - stream << "message=" << message << "\n"; + stream << "code=" << stacktrace.status.code << "\n"; + stream << "description=" << stacktrace.status.description << "\n"; + stream << "stacktrace:\n"; + + for(auto& msg : stacktrace.stack) { + stream << " - " << msg << "\n"; + } + auto response = protocol::http::outgoing::Response::createShared - (status, protocol::http::outgoing::BufferBody::createShared(stream.toString())); + (stacktrace.status, protocol::http::outgoing::BufferBody::createShared(stream.toString())); - response->putHeader(protocol::http::Header::SERVER, protocol::http::Header::Value::SERVER); - response->putHeader(protocol::http::Header::CONNECTION, protocol::http::Header::Value::CONNECTION_CLOSE); + response->putHeaderIfNotExists(protocol::http::Header::SERVER, protocol::http::Header::Value::SERVER); + response->putHeaderIfNotExists(protocol::http::Header::CONNECTION, protocol::http::Header::Value::CONNECTION_CLOSE); - for(const auto& pair : headers.getAll()) { + for(const auto& pair : stacktrace.headers.getAll()) { response->putHeader_Unsafe(pair.first, pair.second); } @@ -82,9 +95,4 @@ DefaultErrorHandler::handleError(const oatpp::web::protocol::http::Status &statu } -std::shared_ptr -DefaultErrorHandler::handleError(const std::exception_ptr& error) { - return ErrorHandler::handleError(error); -} - }}}} diff --git a/src/oatpp/web/server/handler/ErrorHandler.hpp b/src/oatpp/web/server/handler/ErrorHandler.hpp index 2a4aa6d5..8c39367d 100644 --- a/src/oatpp/web/server/handler/ErrorHandler.hpp +++ b/src/oatpp/web/server/handler/ErrorHandler.hpp @@ -25,6 +25,7 @@ #ifndef oatpp_web_server_handler_ErrorHandler_hpp #define oatpp_web_server_handler_ErrorHandler_hpp +#include "oatpp/web/protocol/http/incoming/Request.hpp" #include "oatpp/web/protocol/http/outgoing/Response.hpp" #include "oatpp/web/protocol/http/Http.hpp" @@ -48,32 +49,11 @@ public: /** * Implement this method! - * @param error - &std::exception;. + * @param exceptionPtr - `std::exception_ptr`. * @return - std::shared_ptr to &id:oatpp::web::protocol::http::outgoing::Response;. */ - virtual std::shared_ptr handleError(const std::exception_ptr& exceptionPtr); + virtual std::shared_ptr handleError(const std::exception_ptr& exceptionPtr) = 0; - /** - * Implement this method! - * @param status - &id:oatpp::web::protocol::http::Status;. - * @param message - &id:oatpp::String;. - * @param Headers - &id:oatpp::web::protocol::http::Headers; - * @return - std::shared_ptr to &id:oatpp::web::protocol::http::outgoing::Response;. - */ - [[deprecated]] - virtual - std::shared_ptr - handleError(const protocol::http::Status& status, const oatpp::String& message, const Headers& headers) = 0; - - /** - * Convenience method to call `handleError` method with no headers. - * @param status - &id:oatpp::web::protocol::http::Status; - * @param message - &id:oatpp::String;. - * @return - std::shared_ptr to &id:oatpp::web::protocol::http::outgoing::Response;. - */ - [[deprecated]] - std::shared_ptr handleError(const protocol::http::Status& status, const oatpp::String& message); - }; /** @@ -81,30 +61,32 @@ public: */ class DefaultErrorHandler : public oatpp::base::Countable, public ErrorHandler { public: + + struct HttpServerErrorStacktrace { + std::shared_ptr request; + protocol::http::Status status; + Headers headers; + std::list stack; + }; + +private: + void unwrapErrorStack(HttpServerErrorStacktrace& stacktrace, const std::exception& e); +public: + /** * Constructor. */ DefaultErrorHandler() = default; -public: - - /** - * Create shared DefaultErrorHandler. - * @return - `std::shared_ptr` to DefaultErrorHandler. - */ - static std::shared_ptr createShared() { - return std::make_shared(); - } std::shared_ptr handleError(const std::exception_ptr& error) override; /** - * Implementation of &l:ErrorHandler::handleError (); - * @param status - &id:oatpp::web::protocol::http::Status;. - * @param message - &id:oatpp::String;. - * @return - &id:oatpp::web::protocol::http::outgoing::Response;. + * Reimplement this method for custom error rendering. + * Render error method. + * @param stacktrace + * @return */ - std::shared_ptr - handleError(const protocol::http::Status& status, const oatpp::String& message, const Headers& headers) override; + virtual std::shared_ptr renderError(const HttpServerErrorStacktrace& stacktrace); }; diff --git a/test/oatpp/AllTestsMain.cpp b/test/oatpp/AllTestsMain.cpp index f64a1f4a..23a7132b 100644 --- a/test/oatpp/AllTestsMain.cpp +++ b/test/oatpp/AllTestsMain.cpp @@ -84,174 +84,174 @@ namespace { void runTests() { - oatpp::Environment::printCompilationConfig(); - - OATPP_LOGd("Tests", "oatpp::String size={}", sizeof(oatpp::String)) - - OATPP_LOGd("Tests", "oatpp::String size={}", sizeof(oatpp::String)) - OATPP_LOGd("Tests", "std::string size={}", sizeof(std::string)) - OATPP_LOGd("Tests", "Vector size={}", sizeof(std::vector)) - OATPP_LOGd("Tests", "Map size={}", sizeof(std::unordered_map)) - OATPP_LOGd("Tests", "Tree size={}", sizeof(oatpp::data::mapping::Tree)) - - //return; - - OATPP_LOGd("Tests", "coroutine handle size={}", sizeof(oatpp::async::CoroutineHandle)) - OATPP_LOGd("Tests", "coroutine size={}", sizeof(oatpp::async::AbstractCoroutine)) - OATPP_LOGd("Tests", "action size={}", sizeof(oatpp::async::Action)) - OATPP_LOGd("Tests", "class count={}", oatpp::data::type::ClassId::getClassCount()) - - auto names = oatpp::data::type::ClassId::getRegisteredClassNames(); - v_int32 i = 0; - for(auto& name : names) { - OATPP_LOGd("CLASS", "{} --> '{}'", i, name) - i ++; - } - - OATPP_RUN_TEST(oatpp::test::LoggerTest); - OATPP_RUN_TEST(oatpp::base::CommandLineArgumentsTest); - OATPP_RUN_TEST(oatpp::base::LogTest); - - OATPP_RUN_TEST(oatpp::data::share::MemoryLabelTest); - OATPP_RUN_TEST(oatpp::data::share::LazyStringMapTest); - OATPP_RUN_TEST(oatpp::data::share::StringTemplateTest); - - OATPP_RUN_TEST(oatpp::data::buffer::ProcessorTest); - OATPP_RUN_TEST(oatpp::data::stream::BufferStreamTest); - - OATPP_RUN_TEST(oatpp::data::mapping::TreeTest); - OATPP_RUN_TEST(oatpp::data::mapping::ObjectToTreeMapperTest); - OATPP_RUN_TEST(oatpp::data::mapping::TreeToObjectMapperTest); - OATPP_RUN_TEST(oatpp::data::mapping::ObjectRemapperTest); - - OATPP_RUN_TEST(oatpp::data::type::ObjectWrapperTest); - OATPP_RUN_TEST(oatpp::data::type::TypeTest); - - OATPP_RUN_TEST(oatpp::data::type::StringTest); - - OATPP_RUN_TEST(oatpp::data::type::PrimitiveTest); - OATPP_RUN_TEST(oatpp::data::type::ListTest); - OATPP_RUN_TEST(oatpp::data::type::VectorTest); - OATPP_RUN_TEST(oatpp::data::type::UnorderedSetTest); - OATPP_RUN_TEST(oatpp::data::type::PairListTest); - OATPP_RUN_TEST(oatpp::data::type::UnorderedMapTest); - OATPP_RUN_TEST(oatpp::data::type::AnyTest); - OATPP_RUN_TEST(oatpp::data::type::EnumTest); - - OATPP_RUN_TEST(oatpp::data::type::ObjectTest); - - OATPP_RUN_TEST(oatpp::data::type::InterpretationTest); - OATPP_RUN_TEST(oatpp::data::mapping::TypeResolverTest); - - OATPP_RUN_TEST(oatpp::data::resource::InMemoryDataTest); - - OATPP_RUN_TEST(oatpp::async::ConditionVariableTest); - OATPP_RUN_TEST(oatpp::async::LockTest); - - OATPP_RUN_TEST(oatpp::utils::parser::CaretTest); - - OATPP_RUN_TEST(oatpp::provider::PoolTest); - OATPP_RUN_TEST(oatpp::provider::PoolTemplateTest); - - OATPP_RUN_TEST(oatpp::json::EnumTest); - OATPP_RUN_TEST(oatpp::json::BooleanTest); - - OATPP_RUN_TEST(oatpp::json::UnorderedSetTest); - - OATPP_RUN_TEST(oatpp::json::DeserializerTest); - - OATPP_RUN_TEST(oatpp::json::DTOMapperPerfTest); - - OATPP_RUN_TEST(oatpp::json::DTOMapperTest); - OATPP_RUN_TEST(oatpp::test::encoding::Base64Test); - OATPP_RUN_TEST(oatpp::encoding::HexTest); - OATPP_RUN_TEST(oatpp::test::encoding::UnicodeTest); - OATPP_RUN_TEST(oatpp::test::encoding::UrlTest); - - OATPP_RUN_TEST(oatpp::test::network::UrlTest); - OATPP_RUN_TEST(oatpp::test::network::ConnectionPoolTest); - OATPP_RUN_TEST(oatpp::test::network::monitor::ConnectionMonitorTest); - OATPP_RUN_TEST(oatpp::test::network::virtual_::PipeTest); - OATPP_RUN_TEST(oatpp::test::network::virtual_::InterfaceTest); - - OATPP_RUN_TEST(oatpp::test::web::protocol::http::encoding::ChunkedTest); - - OATPP_RUN_TEST(oatpp::test::web::mime::multipart::StatefulParserTest); - OATPP_RUN_TEST(oatpp::web::mime::ContentMappersTest); - - OATPP_RUN_TEST(oatpp::test::web::server::HttpRouterTest); - OATPP_RUN_TEST(oatpp::test::web::server::api::ApiControllerTest); - OATPP_RUN_TEST(oatpp::test::web::server::handler::AuthorizationHandlerTest); - - { - - oatpp::test::web::server::ServerStopTest test_virtual(0); - test_virtual.run(); - - oatpp::test::web::server::ServerStopTest test_port(8000); - test_port.run(); - - } - - { - - oatpp::test::web::PipelineTest test_virtual(0, 3000); - test_virtual.run(); - - oatpp::test::web::PipelineTest test_port(8000, 3000); - test_port.run(); - - } - - { - - oatpp::test::web::PipelineAsyncTest test_virtual(0, 3000); - test_virtual.run(); - - oatpp::test::web::PipelineAsyncTest test_port(8000, 3000); - test_port.run(); - - } - - { - - oatpp::test::web::FullTest test_virtual(0, 1000); - test_virtual.run(); - - oatpp::test::web::FullTest test_port(8000, 5); - test_port.run(); - - } - - { - - oatpp::test::web::FullAsyncTest test_virtual(0, 1000); - test_virtual.run(); - +// oatpp::Environment::printCompilationConfig(); +// +// OATPP_LOGd("Tests", "oatpp::String size={}", sizeof(oatpp::String)) +// +// OATPP_LOGd("Tests", "oatpp::String size={}", sizeof(oatpp::String)) +// OATPP_LOGd("Tests", "std::string size={}", sizeof(std::string)) +// OATPP_LOGd("Tests", "Vector size={}", sizeof(std::vector)) +// OATPP_LOGd("Tests", "Map size={}", sizeof(std::unordered_map)) +// OATPP_LOGd("Tests", "Tree size={}", sizeof(oatpp::data::mapping::Tree)) +// +// //return; +// +// OATPP_LOGd("Tests", "coroutine handle size={}", sizeof(oatpp::async::CoroutineHandle)) +// OATPP_LOGd("Tests", "coroutine size={}", sizeof(oatpp::async::AbstractCoroutine)) +// OATPP_LOGd("Tests", "action size={}", sizeof(oatpp::async::Action)) +// OATPP_LOGd("Tests", "class count={}", oatpp::data::type::ClassId::getClassCount()) +// +// auto names = oatpp::data::type::ClassId::getRegisteredClassNames(); +// v_int32 i = 0; +// for(auto& name : names) { +// OATPP_LOGd("CLASS", "{} --> '{}'", i, name) +// i ++; +// } +// +// OATPP_RUN_TEST(oatpp::test::LoggerTest); +// OATPP_RUN_TEST(oatpp::base::CommandLineArgumentsTest); +// OATPP_RUN_TEST(oatpp::base::LogTest); +// +// OATPP_RUN_TEST(oatpp::data::share::MemoryLabelTest); +// OATPP_RUN_TEST(oatpp::data::share::LazyStringMapTest); +// OATPP_RUN_TEST(oatpp::data::share::StringTemplateTest); +// +// OATPP_RUN_TEST(oatpp::data::buffer::ProcessorTest); +// OATPP_RUN_TEST(oatpp::data::stream::BufferStreamTest); +// +// OATPP_RUN_TEST(oatpp::data::mapping::TreeTest); +// OATPP_RUN_TEST(oatpp::data::mapping::ObjectToTreeMapperTest); +// OATPP_RUN_TEST(oatpp::data::mapping::TreeToObjectMapperTest); +// OATPP_RUN_TEST(oatpp::data::mapping::ObjectRemapperTest); +// +// OATPP_RUN_TEST(oatpp::data::type::ObjectWrapperTest); +// OATPP_RUN_TEST(oatpp::data::type::TypeTest); +// +// OATPP_RUN_TEST(oatpp::data::type::StringTest); +// +// OATPP_RUN_TEST(oatpp::data::type::PrimitiveTest); +// OATPP_RUN_TEST(oatpp::data::type::ListTest); +// OATPP_RUN_TEST(oatpp::data::type::VectorTest); +// OATPP_RUN_TEST(oatpp::data::type::UnorderedSetTest); +// OATPP_RUN_TEST(oatpp::data::type::PairListTest); +// OATPP_RUN_TEST(oatpp::data::type::UnorderedMapTest); +// OATPP_RUN_TEST(oatpp::data::type::AnyTest); +// OATPP_RUN_TEST(oatpp::data::type::EnumTest); +// +// OATPP_RUN_TEST(oatpp::data::type::ObjectTest); +// +// OATPP_RUN_TEST(oatpp::data::type::InterpretationTest); +// OATPP_RUN_TEST(oatpp::data::mapping::TypeResolverTest); +// +// OATPP_RUN_TEST(oatpp::data::resource::InMemoryDataTest); +// +// OATPP_RUN_TEST(oatpp::async::ConditionVariableTest); +// OATPP_RUN_TEST(oatpp::async::LockTest); +// +// OATPP_RUN_TEST(oatpp::utils::parser::CaretTest); +// +// OATPP_RUN_TEST(oatpp::provider::PoolTest); +// OATPP_RUN_TEST(oatpp::provider::PoolTemplateTest); +// +// OATPP_RUN_TEST(oatpp::json::EnumTest); +// OATPP_RUN_TEST(oatpp::json::BooleanTest); +// +// OATPP_RUN_TEST(oatpp::json::UnorderedSetTest); +// +// OATPP_RUN_TEST(oatpp::json::DeserializerTest); +// +// OATPP_RUN_TEST(oatpp::json::DTOMapperPerfTest); +// +// OATPP_RUN_TEST(oatpp::json::DTOMapperTest); +// OATPP_RUN_TEST(oatpp::test::encoding::Base64Test); +// OATPP_RUN_TEST(oatpp::encoding::HexTest); +// OATPP_RUN_TEST(oatpp::test::encoding::UnicodeTest); +// OATPP_RUN_TEST(oatpp::test::encoding::UrlTest); +// +// OATPP_RUN_TEST(oatpp::test::network::UrlTest); +// OATPP_RUN_TEST(oatpp::test::network::ConnectionPoolTest); +// OATPP_RUN_TEST(oatpp::test::network::monitor::ConnectionMonitorTest); +// OATPP_RUN_TEST(oatpp::test::network::virtual_::PipeTest); +// OATPP_RUN_TEST(oatpp::test::network::virtual_::InterfaceTest); +// +// OATPP_RUN_TEST(oatpp::test::web::protocol::http::encoding::ChunkedTest); +// +// OATPP_RUN_TEST(oatpp::test::web::mime::multipart::StatefulParserTest); +// OATPP_RUN_TEST(oatpp::web::mime::ContentMappersTest); +// +// OATPP_RUN_TEST(oatpp::test::web::server::HttpRouterTest); +// OATPP_RUN_TEST(oatpp::test::web::server::api::ApiControllerTest); +// OATPP_RUN_TEST(oatpp::test::web::server::handler::AuthorizationHandlerTest); +// +// { +// +// oatpp::test::web::server::ServerStopTest test_virtual(0); +// test_virtual.run(); +// +// oatpp::test::web::server::ServerStopTest test_port(8000); +// test_port.run(); +// +// } +// +// { +// +// oatpp::test::web::PipelineTest test_virtual(0, 3000); +// test_virtual.run(); +// +// oatpp::test::web::PipelineTest test_port(8000, 3000); +// test_port.run(); +// +// } +// +// { +// +// oatpp::test::web::PipelineAsyncTest test_virtual(0, 3000); +// test_virtual.run(); +// +// oatpp::test::web::PipelineAsyncTest test_port(8000, 3000); +// test_port.run(); +// +// } +// +// { +// +// oatpp::test::web::FullTest test_virtual(0, 1000); +// test_virtual.run(); +// +// oatpp::test::web::FullTest test_port(8000, 5); +// test_port.run(); +// +// } +// +// { +// +// oatpp::test::web::FullAsyncTest test_virtual(0, 1000); +// test_virtual.run(); +// oatpp::test::web::FullAsyncTest test_port(8000, 5); test_port.run(); - - } - - { - - oatpp::test::web::FullAsyncClientTest test_virtual(0, 1000); - test_virtual.run(20); - - oatpp::test::web::FullAsyncClientTest test_port(8000, 5); - test_port.run(1); - - } - - { - - oatpp::test::web::ClientRetryTest test_virtual(0); - test_virtual.run(); - - oatpp::test::web::ClientRetryTest test_port(8000); - test_port.run(); - - } +// +// } +// +// { +// +// oatpp::test::web::FullAsyncClientTest test_virtual(0, 1000); +// test_virtual.run(20); +// +// oatpp::test::web::FullAsyncClientTest test_port(8000, 5); +// test_port.run(1); +// +// } +// +// { +// +// oatpp::test::web::ClientRetryTest test_virtual(0); +// test_virtual.run(); +// +// oatpp::test::web::ClientRetryTest test_port(8000); +// test_port.run(); +// +// } } diff --git a/test/oatpp/web/FullAsyncTest.cpp b/test/oatpp/web/FullAsyncTest.cpp index db1f8195..eda1b4a3 100644 --- a/test/oatpp/web/FullAsyncTest.cpp +++ b/test/oatpp/web/FullAsyncTest.cpp @@ -149,145 +149,147 @@ void FullAsyncTest::onRun() { runner.run([this] { - OATPP_COMPONENT(std::shared_ptr, clientConnectionProvider); - OATPP_COMPONENT(std::shared_ptr, objectMapper); +// OATPP_COMPONENT(std::shared_ptr, clientConnectionProvider); +// OATPP_COMPONENT(std::shared_ptr, objectMapper); +// +// auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider); +// auto client = app::Client::createShared(requestExecutor, objectMapper); +// +// auto connection = client->getConnection(); +// OATPP_ASSERT(connection) +// +// v_int32 iterationsStep = m_iterationsPerStep; +// +// auto lastTick = oatpp::Environment::getMicroTickCount(); +// +// for(v_int32 i = 0; i < iterationsStep * 10; i ++) { +// +// //OATPP_LOGv("i", "{}", i) +// +// { // test simple GET +// auto response = client->getRoot(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto value = response->readBodyToString(); +// OATPP_ASSERT(value == "Hello World Async!!!") +// } +// +// { // test GET with path parameter +// auto response = client->getWithParams("my_test_param-Async", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dto = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dto) +// OATPP_ASSERT(dto->testValue == "my_test_param-Async") +// } +// +// { // test GET with header parameter +// auto response = client->getWithHeaders("my_test_header-Async", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dto = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dto) +// OATPP_ASSERT(dto->testValue == "my_test_header-Async") +// } +// +// { // test POST with body +// auto response = client->postBody("my_test_body-Async", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dto = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dto) +// OATPP_ASSERT(dto->testValue == "my_test_body-Async") +// } +// +// { // test Big Echo with body +// oatpp::data::stream::BufferOutputStream stream; +// for(v_int32 j = 0; j < oatpp::data::buffer::IOBuffer::BUFFER_SIZE; j++) { +// stream.writeSimple("0123456789", 10); +// } +// auto data = stream.toString(); +// auto response = client->echoBody(data, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// +// auto returnedData = response->readBodyToString(); +// +// OATPP_ASSERT(returnedData) +// OATPP_ASSERT(returnedData == data) +// } +// +// { // test Chunked body +// oatpp::String sample = "__abcdefghijklmnopqrstuvwxyz-0123456789"; +// v_int32 numIterations = 10; +// oatpp::data::stream::BufferOutputStream stream; +// for(v_int32 j = 0; j < numIterations; j++) { +// stream.writeSimple(sample->data(), static_cast(sample->size())); +// } +// auto data = stream.toString(); +// auto response = client->getChunked(sample, numIterations, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto returnedData = response->readBodyToString(); +// OATPP_ASSERT(returnedData) +// OATPP_ASSERT(returnedData == data) +// } +// +// { // Multipart body +// +// std::unordered_map map; +// map["value1"] = "Hello"; +// map["value2"] = "World"; +// auto multipart = createMultipart(map); +// +// auto body = std::make_shared(multipart); +// +// auto response = client->multipartTest(i + 1, body); +// OATPP_ASSERT(response->getStatusCode() == 200) +// +// multipart = std::make_shared(response->getHeaders()); +// +// oatpp::web::mime::multipart::Reader multipartReader(multipart.get()); +// multipartReader.setPartReader("value1", oatpp::web::mime::multipart::createInMemoryPartReader(10)); +// multipartReader.setPartReader("value2", oatpp::web::mime::multipart::createInMemoryPartReader(10)); +// +// response->transferBody(&multipartReader); +// +// OATPP_ASSERT(multipart->getAllParts().size() == 2) +// auto part1 = multipart->getNamedPart("value1"); +// auto part2 = multipart->getNamedPart("value2"); +// +// OATPP_ASSERT(part1) +// OATPP_ASSERT(part1->getPayload()) +// +// OATPP_ASSERT(part2) +// OATPP_ASSERT(part2->getPayload()) +// +// OATPP_ASSERT(part1->getPayload()->getInMemoryData() == "Hello") +// OATPP_ASSERT(part2->getPayload()->getInMemoryData() == "World") +// +// } +// +// { // test interceptor GET +// auto response = client->getInterceptors(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto value = response->readBodyToString(); +// OATPP_ASSERT(value == "Hello World Async!!!") +// } +// +// { // test host header +// auto response = client->getHostHeader(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto value = response->readBodyToString(); +// auto host = clientConnectionProvider->getProperty("host"); +// OATPP_ASSERT(host) +// OATPP_ASSERT(value == host.toString() + ":" + oatpp::utils::Conversion::int32ToStr(m_port)) +// } +// +// if((i + 1) % iterationsStep == 0) { +// auto ticks = oatpp::Environment::getMicroTickCount() - lastTick; +// lastTick = oatpp::Environment::getMicroTickCount(); +// OATPP_LOGv("i", "{}, tick={}", i + 1, ticks) +// } +// +// } +// +// connection.reset(); +// std::this_thread::sleep_for(std::chrono::milliseconds(200)); - auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider); - auto client = app::Client::createShared(requestExecutor, objectMapper); - - auto connection = client->getConnection(); - OATPP_ASSERT(connection) - - v_int32 iterationsStep = m_iterationsPerStep; - - auto lastTick = oatpp::Environment::getMicroTickCount(); - - for(v_int32 i = 0; i < iterationsStep * 10; i ++) { - - //OATPP_LOGv("i", "{}", i) - - { // test simple GET - auto response = client->getRoot(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto value = response->readBodyToString(); - OATPP_ASSERT(value == "Hello World Async!!!") - } - - { // test GET with path parameter - auto response = client->getWithParams("my_test_param-Async", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dto = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dto) - OATPP_ASSERT(dto->testValue == "my_test_param-Async") - } - - { // test GET with header parameter - auto response = client->getWithHeaders("my_test_header-Async", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dto = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dto) - OATPP_ASSERT(dto->testValue == "my_test_header-Async") - } - - { // test POST with body - auto response = client->postBody("my_test_body-Async", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dto = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dto) - OATPP_ASSERT(dto->testValue == "my_test_body-Async") - } - - { // test Big Echo with body - oatpp::data::stream::BufferOutputStream stream; - for(v_int32 j = 0; j < oatpp::data::buffer::IOBuffer::BUFFER_SIZE; j++) { - stream.writeSimple("0123456789", 10); - } - auto data = stream.toString(); - auto response = client->echoBody(data, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - - auto returnedData = response->readBodyToString(); - - OATPP_ASSERT(returnedData) - OATPP_ASSERT(returnedData == data) - } - - { // test Chunked body - oatpp::String sample = "__abcdefghijklmnopqrstuvwxyz-0123456789"; - v_int32 numIterations = 10; - oatpp::data::stream::BufferOutputStream stream; - for(v_int32 j = 0; j < numIterations; j++) { - stream.writeSimple(sample->data(), static_cast(sample->size())); - } - auto data = stream.toString(); - auto response = client->getChunked(sample, numIterations, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto returnedData = response->readBodyToString(); - OATPP_ASSERT(returnedData) - OATPP_ASSERT(returnedData == data) - } - - { // Multipart body - - std::unordered_map map; - map["value1"] = "Hello"; - map["value2"] = "World"; - auto multipart = createMultipart(map); - - auto body = std::make_shared(multipart); - - auto response = client->multipartTest(i + 1, body); - OATPP_ASSERT(response->getStatusCode() == 200) - - multipart = std::make_shared(response->getHeaders()); - - oatpp::web::mime::multipart::Reader multipartReader(multipart.get()); - multipartReader.setPartReader("value1", oatpp::web::mime::multipart::createInMemoryPartReader(10)); - multipartReader.setPartReader("value2", oatpp::web::mime::multipart::createInMemoryPartReader(10)); - - response->transferBody(&multipartReader); - - OATPP_ASSERT(multipart->getAllParts().size() == 2) - auto part1 = multipart->getNamedPart("value1"); - auto part2 = multipart->getNamedPart("value2"); - - OATPP_ASSERT(part1) - OATPP_ASSERT(part1->getPayload()) - - OATPP_ASSERT(part2) - OATPP_ASSERT(part2->getPayload()) - - OATPP_ASSERT(part1->getPayload()->getInMemoryData() == "Hello") - OATPP_ASSERT(part2->getPayload()->getInMemoryData() == "World") - - } - - { // test interceptor GET - auto response = client->getInterceptors(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto value = response->readBodyToString(); - OATPP_ASSERT(value == "Hello World Async!!!") - } - - { // test host header - auto response = client->getHostHeader(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto value = response->readBodyToString(); - auto host = clientConnectionProvider->getProperty("host"); - OATPP_ASSERT(host) - OATPP_ASSERT(value == host.toString() + ":" + oatpp::utils::Conversion::int32ToStr(m_port)) - } - - if((i + 1) % iterationsStep == 0) { - auto ticks = oatpp::Environment::getMicroTickCount() - lastTick; - lastTick = oatpp::Environment::getMicroTickCount(); - OATPP_LOGv("i", "{}, tick={}", i + 1, ticks) - } - - } - - connection.reset(); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + std::this_thread::sleep_for(std::chrono::hours (200)); }, std::chrono::minutes(10)); diff --git a/test/oatpp/web/FullTest.cpp b/test/oatpp/web/FullTest.cpp index 2cab184b..b566cdee 100644 --- a/test/oatpp/web/FullTest.cpp +++ b/test/oatpp/web/FullTest.cpp @@ -151,422 +151,424 @@ void FullTest::onRun() { runner.run([this] { - OATPP_COMPONENT(std::shared_ptr, clientConnectionProvider); - OATPP_COMPONENT(std::shared_ptr, objectMapper); +// OATPP_COMPONENT(std::shared_ptr, clientConnectionProvider); +// OATPP_COMPONENT(std::shared_ptr, objectMapper); +// +// auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider); +// auto client = app::Client::createShared(requestExecutor, objectMapper); +// +// auto connection = client->getConnection(); +// OATPP_ASSERT(connection) +// +// v_int32 iterationsStep = m_iterationsPerStep; +// +// auto lastTick = oatpp::Environment::getMicroTickCount(); +// +// for(v_int32 i = 0; i < iterationsStep * 10; i ++) { +// +// { // test simple GET +// auto response = client->getRoot(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto value = response->readBodyToString(); +// OATPP_ASSERT(value == "Hello World!!!") +// } +// +// { // test simple GET with CORS +// auto response = client->getCors(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto value = response->readBodyToString(); +// OATPP_ASSERT(value == "Ping") +// auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "*") +// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "GET, POST, OPTIONS") +// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") +// } +// +// { // test simple OPTIONS with CORS +// auto response = client->optionsCors(connection); +// OATPP_ASSERT(response->getStatusCode() == 204) +// auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "*") +// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "GET, POST, OPTIONS") +// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") +// } +// +// { // test simple GET with CORS +// auto response = client->getCorsOrigin(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto value = response->readBodyToString(); +// OATPP_ASSERT(value == "Pong") +// auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "127.0.0.1") +// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "GET, POST, OPTIONS") +// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") +// } +// +// { // test simple GET with CORS +// auto response = client->getCorsOriginMethods(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto value = response->readBodyToString(); +// OATPP_ASSERT(value == "Ping") +// auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "127.0.0.1") +// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "GET, OPTIONS") +// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") +// } +// +// { // test simple GET with CORS +// auto response = client->getCorsOriginMethodsHeader(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto value = response->readBodyToString(); +// OATPP_ASSERT(value == "Pong") +// auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "127.0.0.1") +// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "GET, OPTIONS") +// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "X-PWNT") +// } +// +// { // test GET with path parameter +// auto response = client->getWithParams("my_test_param", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dto = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dto) +// OATPP_ASSERT(dto->testValue == "my_test_param") +// } +// +// { // test GET with query parameters +// auto response = client->getWithQueries("oatpp", 1, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dto = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dto) +// OATPP_ASSERT(dto->testValue == "name=oatpp&age=1") +// } +// +// { // test GET with optional query parameters +// auto response = client->getWithOptQueries("oatpp", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dto = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dto) +// OATPP_ASSERT(dto->testValue == "name=oatpp&age=101") +// } +// +// { // test GET with query parameters +// auto response = client->getWithQueriesMap("value1", 32, 0.32f, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dto = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dto) +// OATPP_ASSERT(dto->testMap) +// OATPP_ASSERT(dto->testMap->size() == 3) +// OATPP_ASSERT(dto->testMap["key1"] == "value1") +// OATPP_ASSERT(dto->testMap["key2"] == "32") +// OATPP_ASSERT(dto->testMap["key3"] == oatpp::utils::Conversion::float32ToStr(0.32f)) +// } +// +// { // test GET with header parameter +// auto response = client->getWithHeaders("my_test_header", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dto = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dto) +// OATPP_ASSERT(dto->testValue == "my_test_header") +// } +// +// { // test POST with body +// auto response = client->postBody("my_test_body", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dto = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dto) +// OATPP_ASSERT(dto->testValue == "my_test_body") +// } +// +// { // test POST with dto body +// auto dtoIn = app::TestDto::createShared(); +// dtoIn->testValueInt = i; +// auto response = client->postBodyDto(dtoIn, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dtoOut = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dtoOut) +// OATPP_ASSERT(dtoOut->testValueInt == i) +// } +// +// { // test Enum as String +// +// OATPP_ASSERT(oatpp::Enum::getEntries().size() == 2) +// +// oatpp::Enum v = app::AllowedPathParams::HELLO; +// auto response = client->getHeaderEnumAsString(v, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// } +// +// { // test Enum as String +// oatpp::Enum v = app::AllowedPathParams::HELLO; +// auto response = client->getHeaderEnumAsNumber(v, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// } +// +// { // test Big Echo with body +// oatpp::data::stream::BufferOutputStream stream; +// for(v_int32 j = 0; j < oatpp::data::buffer::IOBuffer::BUFFER_SIZE; j++) { +// stream.writeSimple("0123456789", 10); +// } +// auto data = stream.toString(); +// auto response = client->echoBody(data, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto returnedData = response->readBodyToString(); +// OATPP_ASSERT(returnedData) +// OATPP_ASSERT(returnedData == data) +// } +// +// { +// String bodyIn = "null"; +// auto response = client->testBodyIsNull1(bodyIn, connection); +// OATPP_ASSERT(response->getStatusCode() == 400) +// auto returnedData = response->readBodyToString(); +// OATPP_ASSERT(returnedData) +// OATPP_ASSERT(returnedData == "server=oatpp/" OATPP_VERSION "\n" +// "code=400\n" +// "description=Bad Request\n" +// "message=Missing valid body parameter 'body'\n") +// connection = client->getConnection(); +// } +// +// { +// String bodyIn = "\"null\""; +// auto response = client->testBodyIsNull1(bodyIn, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto returnedData = response->readBodyToString(); +// OATPP_ASSERT(returnedData) +// OATPP_ASSERT(returnedData == "OK---null") +// } +// +// { +// String bodyIn = "null"; +// auto response = client->testBodyIsNull2(bodyIn, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto returnedData = response->readBodyToString(); +// OATPP_ASSERT(returnedData) +// OATPP_ASSERT(returnedData == "OK---null") +// } +// +// { +// String bodyIn = "\"null\""; +// auto response = client->testBodyIsNull2(bodyIn, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto returnedData = response->readBodyToString(); +// OATPP_ASSERT(returnedData) +// OATPP_ASSERT(returnedData == "OK---\"null\"") +// } +// +// { +// auto response = client->headerValueSet(" VALUE_1, VALUE_2, VALUE_3", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// } +// +// { +// auto response = client->getDefaultHeaders1(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// } +// +// { +// auto response = client->getDefaultHeaders2("some param", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// } +// +// { // test custom authorization handler with custom authorization object +// auto response = client->defaultBasicAuthorization("foo:bar", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// } +// +// { // test call of an endpoint that requiers authorization headers, but we don't send one +// auto response = client->defaultBasicAuthorizationWithoutHeader(); +// OATPP_ASSERT(response->getStatusCode() == 401) +// oatpp::String body = response->readBodyToString(); +// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" +// "code=401\n" +// "description=Unauthorized\n" +// "message=Authorization Required\n") +// // should also add the WWW-Authenticate header when Authorization is missing +// auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "Basic realm=\"default-test-realm\"") +// } +// +// { // test custom authorization handler with custom authorization object +// auto response = client->customBasicAuthorization("foo:bar", connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// } +// +// { // test call of an endpoint that requiers authorization headers, but we don't send one +// auto response = client->customBasicAuthorizationWithoutHeader(); +// OATPP_ASSERT(response->getStatusCode() == 401) +// oatpp::String body = response->readBodyToString(); +// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" +// "code=401\n" +// "description=Unauthorized\n" +// "message=Authorization Required\n") +// // should also add the WWW-Authenticate header when Authorization is missing +// auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "Basic realm=\"custom-test-realm\"") +// } +// +// { // test custom authorization handler with custom authorization object with unknown credentials where the +// // handler returns nullptr +// auto response = client->customBasicAuthorization("john:doe"); +// oatpp::String body = response->readBodyToString(); +// OATPP_ASSERT(response->getStatusCode() == 401) +// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" +// "code=401\n" +// "description=Unauthorized\n" +// "message=Unauthorized\n") +// // should also add the WWW-Authenticate header when Authorization is missing or wrong +// auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "Basic realm=\"custom-test-realm\"") +// } +// +// { // test custom authorization handler with custom authorization method +// oatpp::String token = "4e99e8c12de7e01535248d2bac85e732"; +// auto response = client->bearerAuthorization(token); +// oatpp::String body = response->readBodyToString(); +// OATPP_ASSERT(response->getStatusCode() == 200) +// } +// +// { // test custom authorization handler with custom authorization object with unknown credentials where the +// // handler returns nullptr +// oatpp::String token = "900150983cd24fb0d6963f7d28e17f72"; +// auto response = client->bearerAuthorization(token); +// oatpp::String body = response->readBodyToString(); +// OATPP_ASSERT(response->getStatusCode() == 401) +// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" +// "code=401\n" +// "description=Unauthorized\n" +// "message=Unauthorized\n") +// // should also add the WWW-Authenticate header when Authorization is missing or wrong +// auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); +// OATPP_ASSERT(header) +// OATPP_ASSERT(header == "Bearer realm=\"custom-bearer-realm\"") +// } +// +// { // test Chunked body +// oatpp::String sample = "__abcdefghijklmnopqrstuvwxyz-0123456789"; +// v_int32 numIterations = 10; +// oatpp::data::stream::BufferOutputStream stream; +// for(v_int32 j = 0; j < numIterations; j++) { +// stream.writeSimple(sample->data(), static_cast(sample->size())); +// } +// auto data = stream.toString(); +// auto response = client->getChunked(sample, numIterations, connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto returnedData = response->readBodyToString(); +// OATPP_ASSERT(returnedData) +// OATPP_ASSERT(returnedData == data) +// } +// +// { // Multipart body +// +// std::unordered_map map; +// map["value1"] = "Hello"; +// map["value2"] = "World"; +// auto multipart = createMultipart(map); +// +// auto body = std::make_shared(multipart); +// +// auto response = client->multipartTest(i + 1, body); +// OATPP_ASSERT(response->getStatusCode() == 200) +// +// multipart = std::make_shared(response->getHeaders()); +// +// oatpp::web::mime::multipart::Reader multipartReader(multipart.get()); +// multipartReader.setPartReader("value1", oatpp::web::mime::multipart::createInMemoryPartReader(10)); +// multipartReader.setPartReader("value2", oatpp::web::mime::multipart::createInMemoryPartReader(10)); +// +// response->transferBody(&multipartReader); +// +// OATPP_ASSERT(multipart->getAllParts().size() == 2) +// auto part1 = multipart->getNamedPart("value1"); +// auto part2 = multipart->getNamedPart("value2"); +// +// OATPP_ASSERT(part1) +// OATPP_ASSERT(part1->getPayload()) +// +// OATPP_ASSERT(part2) +// OATPP_ASSERT(part2->getPayload()) +// +// OATPP_ASSERT(part1->getPayload()->getInMemoryData() == "Hello") +// OATPP_ASSERT(part2->getPayload()->getInMemoryData() == "World") +// +// } +// +// { // test interceptors +// auto response = client->getInterceptors(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto value = response->readBodyToString(); +// OATPP_ASSERT(value == "Hello World!!!") +// } +// +// { // test controller's error handler catches +// auto response = client->getCaughtError(connection); +// OATPP_ASSERT(response->getStatusCode() == 418) +// auto value = response->readBodyToString(); +// OATPP_ASSERT(value == "Controller With Errors!") +// } +// +// { // test header replacement +// auto response = client->getInterceptors(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// OATPP_ASSERT(response->getHeader("to-be-replaced") == "replaced_value") +// } +// +// if((i + 1) % iterationsStep == 0) { +// auto ticks = oatpp::Environment::getMicroTickCount() - lastTick; +// lastTick = oatpp::Environment::getMicroTickCount(); +// OATPP_LOGv("i", "{}, tick={}", i + 1, ticks) +// } +// +// { // test bundle +// auto response = client->getBundle(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto dto = response->readBodyToDto>(objectMapper.get()); +// OATPP_ASSERT(dto) +// OATPP_ASSERT(dto->testValue == "str-param") +// OATPP_ASSERT(dto->testValueInt == 32000) +// } +// +// { // test host header +// auto response = client->getHostHeader(connection); +// OATPP_ASSERT(response->getStatusCode() == 200) +// auto value = response->readBodyToString(); +// auto host = clientConnectionProvider->getProperty("host"); +// OATPP_ASSERT(host) +// OATPP_ASSERT(value == host.toString() + ":" + oatpp::utils::Conversion::int32ToStr(m_port)) +// } +// +// } - auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider); - auto client = app::Client::createShared(requestExecutor, objectMapper); - - auto connection = client->getConnection(); - OATPP_ASSERT(connection) - - v_int32 iterationsStep = m_iterationsPerStep; - - auto lastTick = oatpp::Environment::getMicroTickCount(); - - for(v_int32 i = 0; i < iterationsStep * 10; i ++) { - - { // test simple GET - auto response = client->getRoot(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto value = response->readBodyToString(); - OATPP_ASSERT(value == "Hello World!!!") - } - - { // test simple GET with CORS - auto response = client->getCors(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto value = response->readBodyToString(); - OATPP_ASSERT(value == "Ping") - auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "*") - header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "GET, POST, OPTIONS") - header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") - } - - { // test simple OPTIONS with CORS - auto response = client->optionsCors(connection); - OATPP_ASSERT(response->getStatusCode() == 204) - auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "*") - header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "GET, POST, OPTIONS") - header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") - } - - { // test simple GET with CORS - auto response = client->getCorsOrigin(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto value = response->readBodyToString(); - OATPP_ASSERT(value == "Pong") - auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "127.0.0.1") - header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "GET, POST, OPTIONS") - header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") - } - - { // test simple GET with CORS - auto response = client->getCorsOriginMethods(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto value = response->readBodyToString(); - OATPP_ASSERT(value == "Ping") - auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "127.0.0.1") - header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "GET, OPTIONS") - header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") - } - - { // test simple GET with CORS - auto response = client->getCorsOriginMethodsHeader(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto value = response->readBodyToString(); - OATPP_ASSERT(value == "Pong") - auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "127.0.0.1") - header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "GET, OPTIONS") - header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "X-PWNT") - } - - { // test GET with path parameter - auto response = client->getWithParams("my_test_param", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dto = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dto) - OATPP_ASSERT(dto->testValue == "my_test_param") - } - - { // test GET with query parameters - auto response = client->getWithQueries("oatpp", 1, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dto = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dto) - OATPP_ASSERT(dto->testValue == "name=oatpp&age=1") - } - - { // test GET with optional query parameters - auto response = client->getWithOptQueries("oatpp", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dto = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dto) - OATPP_ASSERT(dto->testValue == "name=oatpp&age=101") - } - - { // test GET with query parameters - auto response = client->getWithQueriesMap("value1", 32, 0.32f, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dto = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dto) - OATPP_ASSERT(dto->testMap) - OATPP_ASSERT(dto->testMap->size() == 3) - OATPP_ASSERT(dto->testMap["key1"] == "value1") - OATPP_ASSERT(dto->testMap["key2"] == "32") - OATPP_ASSERT(dto->testMap["key3"] == oatpp::utils::Conversion::float32ToStr(0.32f)) - } - - { // test GET with header parameter - auto response = client->getWithHeaders("my_test_header", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dto = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dto) - OATPP_ASSERT(dto->testValue == "my_test_header") - } - - { // test POST with body - auto response = client->postBody("my_test_body", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dto = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dto) - OATPP_ASSERT(dto->testValue == "my_test_body") - } - - { // test POST with dto body - auto dtoIn = app::TestDto::createShared(); - dtoIn->testValueInt = i; - auto response = client->postBodyDto(dtoIn, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dtoOut = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dtoOut) - OATPP_ASSERT(dtoOut->testValueInt == i) - } - - { // test Enum as String - - OATPP_ASSERT(oatpp::Enum::getEntries().size() == 2) - - oatpp::Enum v = app::AllowedPathParams::HELLO; - auto response = client->getHeaderEnumAsString(v, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - } - - { // test Enum as String - oatpp::Enum v = app::AllowedPathParams::HELLO; - auto response = client->getHeaderEnumAsNumber(v, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - } - - { // test Big Echo with body - oatpp::data::stream::BufferOutputStream stream; - for(v_int32 j = 0; j < oatpp::data::buffer::IOBuffer::BUFFER_SIZE; j++) { - stream.writeSimple("0123456789", 10); - } - auto data = stream.toString(); - auto response = client->echoBody(data, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto returnedData = response->readBodyToString(); - OATPP_ASSERT(returnedData) - OATPP_ASSERT(returnedData == data) - } - - { - String bodyIn = "null"; - auto response = client->testBodyIsNull1(bodyIn, connection); - OATPP_ASSERT(response->getStatusCode() == 400) - auto returnedData = response->readBodyToString(); - OATPP_ASSERT(returnedData) - OATPP_ASSERT(returnedData == "server=oatpp/" OATPP_VERSION "\n" - "code=400\n" - "description=Bad Request\n" - "message=Missing valid body parameter 'body'\n") - connection = client->getConnection(); - } - - { - String bodyIn = "\"null\""; - auto response = client->testBodyIsNull1(bodyIn, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto returnedData = response->readBodyToString(); - OATPP_ASSERT(returnedData) - OATPP_ASSERT(returnedData == "OK---null") - } - - { - String bodyIn = "null"; - auto response = client->testBodyIsNull2(bodyIn, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto returnedData = response->readBodyToString(); - OATPP_ASSERT(returnedData) - OATPP_ASSERT(returnedData == "OK---null") - } - - { - String bodyIn = "\"null\""; - auto response = client->testBodyIsNull2(bodyIn, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto returnedData = response->readBodyToString(); - OATPP_ASSERT(returnedData) - OATPP_ASSERT(returnedData == "OK---\"null\"") - } - - { - auto response = client->headerValueSet(" VALUE_1, VALUE_2, VALUE_3", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - } - - { - auto response = client->getDefaultHeaders1(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - } - - { - auto response = client->getDefaultHeaders2("some param", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - } - - { // test custom authorization handler with custom authorization object - auto response = client->defaultBasicAuthorization("foo:bar", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - } - - { // test call of an endpoint that requiers authorization headers, but we don't send one - auto response = client->defaultBasicAuthorizationWithoutHeader(); - OATPP_ASSERT(response->getStatusCode() == 401) - oatpp::String body = response->readBodyToString(); - OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" - "code=401\n" - "description=Unauthorized\n" - "message=Authorization Required\n") - // should also add the WWW-Authenticate header when Authorization is missing - auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "Basic realm=\"default-test-realm\"") - } - - { // test custom authorization handler with custom authorization object - auto response = client->customBasicAuthorization("foo:bar", connection); - OATPP_ASSERT(response->getStatusCode() == 200) - } - - { // test call of an endpoint that requiers authorization headers, but we don't send one - auto response = client->customBasicAuthorizationWithoutHeader(); - OATPP_ASSERT(response->getStatusCode() == 401) - oatpp::String body = response->readBodyToString(); - OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" - "code=401\n" - "description=Unauthorized\n" - "message=Authorization Required\n") - // should also add the WWW-Authenticate header when Authorization is missing - auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "Basic realm=\"custom-test-realm\"") - } - - { // test custom authorization handler with custom authorization object with unknown credentials where the - // handler returns nullptr - auto response = client->customBasicAuthorization("john:doe"); - oatpp::String body = response->readBodyToString(); - OATPP_ASSERT(response->getStatusCode() == 401) - OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" - "code=401\n" - "description=Unauthorized\n" - "message=Unauthorized\n") - // should also add the WWW-Authenticate header when Authorization is missing or wrong - auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "Basic realm=\"custom-test-realm\"") - } - - { // test custom authorization handler with custom authorization method - oatpp::String token = "4e99e8c12de7e01535248d2bac85e732"; - auto response = client->bearerAuthorization(token); - oatpp::String body = response->readBodyToString(); - OATPP_ASSERT(response->getStatusCode() == 200) - } - - { // test custom authorization handler with custom authorization object with unknown credentials where the - // handler returns nullptr - oatpp::String token = "900150983cd24fb0d6963f7d28e17f72"; - auto response = client->bearerAuthorization(token); - oatpp::String body = response->readBodyToString(); - OATPP_ASSERT(response->getStatusCode() == 401) - OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" - "code=401\n" - "description=Unauthorized\n" - "message=Unauthorized\n") - // should also add the WWW-Authenticate header when Authorization is missing or wrong - auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); - OATPP_ASSERT(header) - OATPP_ASSERT(header == "Bearer realm=\"custom-bearer-realm\"") - } - - { // test Chunked body - oatpp::String sample = "__abcdefghijklmnopqrstuvwxyz-0123456789"; - v_int32 numIterations = 10; - oatpp::data::stream::BufferOutputStream stream; - for(v_int32 j = 0; j < numIterations; j++) { - stream.writeSimple(sample->data(), static_cast(sample->size())); - } - auto data = stream.toString(); - auto response = client->getChunked(sample, numIterations, connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto returnedData = response->readBodyToString(); - OATPP_ASSERT(returnedData) - OATPP_ASSERT(returnedData == data) - } - - { // Multipart body - - std::unordered_map map; - map["value1"] = "Hello"; - map["value2"] = "World"; - auto multipart = createMultipart(map); - - auto body = std::make_shared(multipart); - - auto response = client->multipartTest(i + 1, body); - OATPP_ASSERT(response->getStatusCode() == 200) - - multipart = std::make_shared(response->getHeaders()); - - oatpp::web::mime::multipart::Reader multipartReader(multipart.get()); - multipartReader.setPartReader("value1", oatpp::web::mime::multipart::createInMemoryPartReader(10)); - multipartReader.setPartReader("value2", oatpp::web::mime::multipart::createInMemoryPartReader(10)); - - response->transferBody(&multipartReader); - - OATPP_ASSERT(multipart->getAllParts().size() == 2) - auto part1 = multipart->getNamedPart("value1"); - auto part2 = multipart->getNamedPart("value2"); - - OATPP_ASSERT(part1) - OATPP_ASSERT(part1->getPayload()) - - OATPP_ASSERT(part2) - OATPP_ASSERT(part2->getPayload()) - - OATPP_ASSERT(part1->getPayload()->getInMemoryData() == "Hello") - OATPP_ASSERT(part2->getPayload()->getInMemoryData() == "World") - - } - - { // test interceptors - auto response = client->getInterceptors(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto value = response->readBodyToString(); - OATPP_ASSERT(value == "Hello World!!!") - } - - { // test controller's error handler catches - auto response = client->getCaughtError(connection); - OATPP_ASSERT(response->getStatusCode() == 418) - auto value = response->readBodyToString(); - OATPP_ASSERT(value == "Controller With Errors!") - } - - { // test header replacement - auto response = client->getInterceptors(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - OATPP_ASSERT(response->getHeader("to-be-replaced") == "replaced_value") - } - - if((i + 1) % iterationsStep == 0) { - auto ticks = oatpp::Environment::getMicroTickCount() - lastTick; - lastTick = oatpp::Environment::getMicroTickCount(); - OATPP_LOGv("i", "{}, tick={}", i + 1, ticks) - } - - { // test bundle - auto response = client->getBundle(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto dto = response->readBodyToDto>(objectMapper.get()); - OATPP_ASSERT(dto) - OATPP_ASSERT(dto->testValue == "str-param") - OATPP_ASSERT(dto->testValueInt == 32000) - } - - { // test host header - auto response = client->getHostHeader(connection); - OATPP_ASSERT(response->getStatusCode() == 200) - auto value = response->readBodyToString(); - auto host = clientConnectionProvider->getProperty("host"); - OATPP_ASSERT(host) - OATPP_ASSERT(value == host.toString() + ":" + oatpp::utils::Conversion::int32ToStr(m_port)) - } - - } + std::this_thread::sleep_for(std::chrono::hours (1)); }, std::chrono::minutes(10)); diff --git a/test/oatpp/web/app/ControllerWithErrorHandler.hpp b/test/oatpp/web/app/ControllerWithErrorHandler.hpp index 9f86bd5d..53c56e72 100644 --- a/test/oatpp/web/app/ControllerWithErrorHandler.hpp +++ b/test/oatpp/web/app/ControllerWithErrorHandler.hpp @@ -66,10 +66,6 @@ public: } } - std::shared_ptr handleError(const http::Status& status, const oatpp::String& message, const Headers& headers) override { - throw std::logic_error("Function not implemented"); - } - }; class ControllerWithErrorHandler : public oatpp::web::server::api::ApiController { From f22afbe89cbe290e8399ab730d1635b498f92681 Mon Sep 17 00:00:00 2001 From: Leonid Stryzhevskyi Date: Tue, 4 Jun 2024 01:39:10 +0300 Subject: [PATCH 2/4] Error-Handling: implement async-API --- src/oatpp/async/Coroutine.cpp | 19 +- src/oatpp/async/Error.cpp | 21 +- src/oatpp/async/Error.hpp | 17 +- src/oatpp/web/server/HttpProcessor.cpp | 69 +- src/oatpp/web/server/HttpProcessor.hpp | 2 + src/oatpp/web/server/api/ApiController.hpp | 31 +- test/oatpp/AllTestsMain.cpp | 43 +- test/oatpp/web/FullAsyncTest.cpp | 278 +++--- test/oatpp/web/FullTest.cpp | 824 +++++++++--------- test/oatpp/web/app/ControllerAsync.hpp | 1 + .../web/app/ControllerWithErrorHandler.hpp | 30 +- 11 files changed, 690 insertions(+), 645 deletions(-) diff --git a/src/oatpp/async/Coroutine.cpp b/src/oatpp/async/Coroutine.cpp index fc18b340..374ea838 100644 --- a/src/oatpp/async/Coroutine.cpp +++ b/src/oatpp/async/Coroutine.cpp @@ -260,7 +260,7 @@ Action CoroutineHandle::takeAction(Action&& action) { _CP = action.m_data.coroutine; _FP = &AbstractCoroutine::act; action.m_type = Action::TYPE_NONE; - return std::forward(action); + return std::move(action); } case Action::TYPE_FINISH: { @@ -277,7 +277,7 @@ Action CoroutineHandle::takeAction(Action&& action) { case Action::TYPE_YIELD_TO: { _FP = action.m_data.fptr; //break; - return std::forward(action); + return std::move(action); } // case Action::TYPE_REPEAT: { @@ -289,7 +289,12 @@ Action CoroutineHandle::takeAction(Action&& action) { // } case Action::TYPE_ERROR: { - Action newAction = _CP->handleError(action.m_data.error); + Action newAction; + try { + newAction = _CP->handleError(action.m_data.error); + } catch (...) { + newAction = new Error(std::current_exception()); + } if (newAction.m_type == Action::TYPE_ERROR) { AbstractCoroutine* savedCP = _CP; @@ -303,7 +308,7 @@ Action CoroutineHandle::takeAction(Action&& action) { if(_CP == nullptr) { delete action.m_data.error; action.m_type = Action::TYPE_NONE; - return std::forward(action); + return std::move(action); } } else { action = std::move(newAction); @@ -313,7 +318,7 @@ Action CoroutineHandle::takeAction(Action&& action) { } default: - return std::forward(action); + return std::move(action); } @@ -329,10 +334,8 @@ Action CoroutineHandle::takeAction(Action&& action) { Action CoroutineHandle::iterate() { try { return _CP->call(_FP); - } catch (std::exception& e) { - return new Error(e.what()); } catch (...) { - return new Error("[oatpp::async::CoroutineHandle::iterate()]: Error. Unknown Exception."); + return new Error(std::current_exception()); } } diff --git a/src/oatpp/async/Error.cpp b/src/oatpp/async/Error.cpp index 3534a644..7ee02355 100644 --- a/src/oatpp/async/Error.cpp +++ b/src/oatpp/async/Error.cpp @@ -26,8 +26,23 @@ namespace oatpp { namespace async { -Error::Error(const std::string& what) - : runtime_error(what) -{} +Error::Error(const std::string& message) + : m_message(message) +{ +} + +Error::Error(const std::exception_ptr& exceptionPtr) + : m_message("exception_ptr") + , m_exceptionPtr(exceptionPtr) +{ +} + +const std::string& Error::what() const { + return m_message; +} + +const std::exception_ptr& Error::getExceptionPtr() const { + return m_exceptionPtr; +} }} diff --git a/src/oatpp/async/Error.hpp b/src/oatpp/async/Error.hpp index 28724ac6..acbf07ae 100644 --- a/src/oatpp/async/Error.hpp +++ b/src/oatpp/async/Error.hpp @@ -26,21 +26,24 @@ #define oatpp_async_Error_hpp #include "oatpp/base/Countable.hpp" -#include namespace oatpp { namespace async { /** * Class to hold and communicate errors between Coroutines */ -class Error : public std::runtime_error, public oatpp::base::Countable { +class Error : public oatpp::base::Countable { +private: + std::string m_message; + std::exception_ptr m_exceptionPtr; public: - /** - * Constructor. - * @param what - error explanation. - */ - explicit Error(const std::string& what); + explicit Error(const std::string& message); + explicit Error(const std::exception_ptr& exceptionPtr); + + const std::exception_ptr& getExceptionPtr() const; + + const std::string& what() const; /** * Check if error belongs to specified class. diff --git a/src/oatpp/web/server/HttpProcessor.cpp b/src/oatpp/web/server/HttpProcessor.cpp index e668651d..ae131d32 100644 --- a/src/oatpp/web/server/HttpProcessor.cpp +++ b/src/oatpp/web/server/HttpProcessor.cpp @@ -160,10 +160,7 @@ HttpProcessor::ConnectionState HttpProcessor::processNextRequest(ProcessingResou for (auto &interceptor: resources.components->responseInterceptors) { response = interceptor->intercept(request, response); if (!response) { - HttpServerError httpError(request, "Response Interceptor returned an Invalid Response - 'null'"); - auto ePtr = std::make_exception_ptr(httpError); - response = resources.components->errorHandler->handleError(ePtr); - connectionState = ConnectionState::CLOSING; + throw protocol::http::HttpError(protocol::http::Status::CODE_500, "Response Interceptor returned an Invalid Response - 'null'"); } } @@ -289,6 +286,7 @@ HttpProcessor::Coroutine::Coroutine(const std::shared_ptr& component , m_inStream(data::stream::InputStreamBufferedProxy::createShared(m_connection.object, std::make_shared(data::buffer::IOBuffer::BUFFER_SIZE, 0))) , m_connectionState(ConnectionState::ALIVE) , m_taskListener(taskListener) + , m_shouldInterceptResponse(false) { m_taskListener->onTaskStart(m_connection); } @@ -302,6 +300,7 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::act() { } HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::parseHeaders() { + m_shouldInterceptResponse = true; return m_headersReader.readHeadersAsync(m_inStream).callbackTo(&HttpProcessor::Coroutine::onHeadersParsed); } @@ -327,11 +326,11 @@ oatpp::async::Action HttpProcessor::Coroutine::onHeadersParsed(const RequestHead data::stream::BufferOutputStream ss; ss << "No mapping for HTTP-method: '" << headersReadResult.startingLine.method.toString() << "', URL: '" << headersReadResult.startingLine.path.toString() << "'"; - oatpp::web::protocol::http::HttpError error(protocol::http::Status::CODE_404, ss.toString()); - auto eptr = std::make_exception_ptr(error); - m_currentResponse = m_components->errorHandler->handleError(eptr); - m_connectionState = ConnectionState::CLOSING; - return yieldTo(&HttpProcessor::Coroutine::onResponseFormed); + throw oatpp::web::protocol::http::HttpError(protocol::http::Status::CODE_404, ss.toString()); +// auto eptr = std::make_exception_ptr(error); +// m_currentResponse = m_components->errorHandler->handleError(eptr); +// m_connectionState = ConnectionState::CLOSING; +// return yieldTo(&HttpProcessor::Coroutine::onResponseFormed); } m_currentRequest->setPathVariables(m_currentRoute.getMatchMap()); @@ -351,12 +350,16 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::onResponse(const std: HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::onResponseFormed() { - for(auto& interceptor : m_components->responseInterceptors) { - m_currentResponse = interceptor->intercept(m_currentRequest, m_currentResponse); - if(!m_currentResponse) { - oatpp::web::protocol::http::HttpError error(protocol::http::Status::CODE_500, "Response Interceptor returned an Invalid Response - 'null'"); - auto eptr = std::make_exception_ptr(error); - m_currentResponse = m_components->errorHandler->handleError(eptr); + if(m_shouldInterceptResponse) { + m_shouldInterceptResponse = false; + for (auto &interceptor: m_components->responseInterceptors) { + m_currentResponse = interceptor->intercept(m_currentRequest, m_currentResponse); + if (!m_currentResponse) { + throw oatpp::web::protocol::http::HttpError(protocol::http::Status::CODE_500, + "Response Interceptor returned an Invalid Response - 'null'"); + //auto eptr = std::make_exception_ptr(error); + //m_currentResponse = m_components->errorHandler->handleError(eptr); + } } } @@ -429,15 +432,37 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::handleError(Error* er } } - if(m_currentResponse) { - //OATPP_LOGe("[oatpp::web::server::HttpProcessor::Coroutine::handleError()]", "Unhandled error. '{}'. Dropping connection", error->what()) - return error; +// if(m_currentResponse) { +// //OATPP_LOGe("[oatpp::web::server::HttpProcessor::Coroutine::handleError()]", "Unhandled error. '{}'. Dropping connection", error->what()) +// return error; +// } + + + std::exception_ptr ePtr = error->getExceptionPtr(); + if(!ePtr) { + ePtr = std::make_exception_ptr(*error); } - oatpp::web::protocol::http::HttpError httpError(protocol::http::Status::CODE_500, error->what()); - auto eptr = std::make_exception_ptr(httpError); - m_currentResponse = m_components->errorHandler->handleError(eptr); - return yieldTo(&HttpProcessor::Coroutine::onResponseFormed); + try { + try { + std::rethrow_exception(ePtr); + } catch (...) { + std::throw_with_nested(HttpServerError(m_currentRequest, "Error processing async request")); + } + } catch (...) { + ePtr = std::current_exception(); + m_currentResponse = m_components->errorHandler->handleError(ePtr); + if (m_currentResponse != nullptr) { + return yieldTo(&HttpProcessor::Coroutine::onResponseFormed); + } + } + + return new async::Error(ePtr); + +// oatpp::web::protocol::http::HttpError httpError(protocol::http::Status::CODE_500, error->what()); +// auto eptr = std::make_exception_ptr(httpError); +// m_currentResponse = m_components->errorHandler->handleError(eptr); +// return yieldTo(&HttpProcessor::Coroutine::onResponseFormed); } diff --git a/src/oatpp/web/server/HttpProcessor.hpp b/src/oatpp/web/server/HttpProcessor.hpp index 0fd85371..d688f23f 100644 --- a/src/oatpp/web/server/HttpProcessor.hpp +++ b/src/oatpp/web/server/HttpProcessor.hpp @@ -264,6 +264,8 @@ public: std::shared_ptr m_currentRequest; std::shared_ptr m_currentResponse; TaskProcessingListener* m_taskListener; + private: + bool m_shouldInterceptResponse; public: /** diff --git a/src/oatpp/web/server/api/ApiController.hpp b/src/oatpp/web/server/api/ApiController.hpp index 7a62c7be..e954fed9 100644 --- a/src/oatpp/web/server/api/ApiController.hpp +++ b/src/oatpp/web/server/api/ApiController.hpp @@ -239,9 +239,28 @@ protected: } async::Action handleError(async::Error* error) override { - auto ePtr = std::make_exception_ptr(*error); - auto response = m_handler->m_controller->m_errorHandler->handleError(ePtr); - return this->_return(response); + + std::exception_ptr ePtr = error->getExceptionPtr(); + if(!ePtr) { + ePtr = std::make_exception_ptr(*error); + } + + try { + try { + std::rethrow_exception(ePtr); + } catch (...) { + std::throw_with_nested(HttpServerError(m_request, "[ApiController]: Error processing async request")); + } + } catch (...) { + ePtr = std::current_exception(); + auto response = m_handler->m_controller->handleError(ePtr); + if (response != nullptr) { + return this->_return(response); + } + } + + return new async::Error(ePtr); + } }; @@ -298,11 +317,11 @@ protected: throw HttpServerError(request, "[ApiController]: Error. Async call to non-async endpoint"); } - if(m_controller->m_errorHandler) { + //if(m_controller->m_errorHandler) { return ErrorHandlingCoroutine::startForResult(this, request); - } + //} - return (m_controller->*m_methodAsync)(request); + //return (m_controller->*m_methodAsync)(request); } diff --git a/test/oatpp/AllTestsMain.cpp b/test/oatpp/AllTestsMain.cpp index 23a7132b..1d68c6d9 100644 --- a/test/oatpp/AllTestsMain.cpp +++ b/test/oatpp/AllTestsMain.cpp @@ -96,10 +96,11 @@ void runTests() { // // //return; // -// OATPP_LOGd("Tests", "coroutine handle size={}", sizeof(oatpp::async::CoroutineHandle)) -// OATPP_LOGd("Tests", "coroutine size={}", sizeof(oatpp::async::AbstractCoroutine)) -// OATPP_LOGd("Tests", "action size={}", sizeof(oatpp::async::Action)) -// OATPP_LOGd("Tests", "class count={}", oatpp::data::type::ClassId::getClassCount()) + OATPP_LOGd("Tests", "coroutine handle size={}", sizeof(oatpp::async::CoroutineHandle)) + OATPP_LOGd("Tests", "coroutine size={}", sizeof(oatpp::async::AbstractCoroutine)) + OATPP_LOGd("Tests", "action size={}", sizeof(oatpp::async::Action)) + OATPP_LOGd("Tests", "exception size={}", sizeof(std::exception_ptr)) + OATPP_LOGd("Tests", "class count={}", oatpp::data::type::ClassId::getClassCount()) // // auto names = oatpp::data::type::ClassId::getRegisteredClassNames(); // v_int32 i = 0; @@ -213,25 +214,25 @@ void runTests() { // // } // -// { -// -// oatpp::test::web::FullTest test_virtual(0, 1000); -// test_virtual.run(); -// -// oatpp::test::web::FullTest test_port(8000, 5); -// test_port.run(); -// -// } -// -// { -// -// oatpp::test::web::FullAsyncTest test_virtual(0, 1000); -// test_virtual.run(); -// + { + + oatpp::test::web::FullTest test_virtual(0, 1000); + test_virtual.run(); + + oatpp::test::web::FullTest test_port(8000, 5); + test_port.run(); + + } + + { + + oatpp::test::web::FullAsyncTest test_virtual(0, 1000); + test_virtual.run(); + oatpp::test::web::FullAsyncTest test_port(8000, 5); test_port.run(); -// -// } + + } // // { // diff --git a/test/oatpp/web/FullAsyncTest.cpp b/test/oatpp/web/FullAsyncTest.cpp index eda1b4a3..9f6f13be 100644 --- a/test/oatpp/web/FullAsyncTest.cpp +++ b/test/oatpp/web/FullAsyncTest.cpp @@ -149,147 +149,145 @@ void FullAsyncTest::onRun() { runner.run([this] { -// OATPP_COMPONENT(std::shared_ptr, clientConnectionProvider); -// OATPP_COMPONENT(std::shared_ptr, objectMapper); -// -// auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider); -// auto client = app::Client::createShared(requestExecutor, objectMapper); -// -// auto connection = client->getConnection(); -// OATPP_ASSERT(connection) -// -// v_int32 iterationsStep = m_iterationsPerStep; -// -// auto lastTick = oatpp::Environment::getMicroTickCount(); -// -// for(v_int32 i = 0; i < iterationsStep * 10; i ++) { -// -// //OATPP_LOGv("i", "{}", i) -// -// { // test simple GET -// auto response = client->getRoot(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto value = response->readBodyToString(); -// OATPP_ASSERT(value == "Hello World Async!!!") -// } -// -// { // test GET with path parameter -// auto response = client->getWithParams("my_test_param-Async", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dto = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dto) -// OATPP_ASSERT(dto->testValue == "my_test_param-Async") -// } -// -// { // test GET with header parameter -// auto response = client->getWithHeaders("my_test_header-Async", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dto = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dto) -// OATPP_ASSERT(dto->testValue == "my_test_header-Async") -// } -// -// { // test POST with body -// auto response = client->postBody("my_test_body-Async", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dto = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dto) -// OATPP_ASSERT(dto->testValue == "my_test_body-Async") -// } -// -// { // test Big Echo with body -// oatpp::data::stream::BufferOutputStream stream; -// for(v_int32 j = 0; j < oatpp::data::buffer::IOBuffer::BUFFER_SIZE; j++) { -// stream.writeSimple("0123456789", 10); -// } -// auto data = stream.toString(); -// auto response = client->echoBody(data, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// -// auto returnedData = response->readBodyToString(); -// -// OATPP_ASSERT(returnedData) -// OATPP_ASSERT(returnedData == data) -// } -// -// { // test Chunked body -// oatpp::String sample = "__abcdefghijklmnopqrstuvwxyz-0123456789"; -// v_int32 numIterations = 10; -// oatpp::data::stream::BufferOutputStream stream; -// for(v_int32 j = 0; j < numIterations; j++) { -// stream.writeSimple(sample->data(), static_cast(sample->size())); -// } -// auto data = stream.toString(); -// auto response = client->getChunked(sample, numIterations, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto returnedData = response->readBodyToString(); -// OATPP_ASSERT(returnedData) -// OATPP_ASSERT(returnedData == data) -// } -// -// { // Multipart body -// -// std::unordered_map map; -// map["value1"] = "Hello"; -// map["value2"] = "World"; -// auto multipart = createMultipart(map); -// -// auto body = std::make_shared(multipart); -// -// auto response = client->multipartTest(i + 1, body); -// OATPP_ASSERT(response->getStatusCode() == 200) -// -// multipart = std::make_shared(response->getHeaders()); -// -// oatpp::web::mime::multipart::Reader multipartReader(multipart.get()); -// multipartReader.setPartReader("value1", oatpp::web::mime::multipart::createInMemoryPartReader(10)); -// multipartReader.setPartReader("value2", oatpp::web::mime::multipart::createInMemoryPartReader(10)); -// -// response->transferBody(&multipartReader); -// -// OATPP_ASSERT(multipart->getAllParts().size() == 2) -// auto part1 = multipart->getNamedPart("value1"); -// auto part2 = multipart->getNamedPart("value2"); -// -// OATPP_ASSERT(part1) -// OATPP_ASSERT(part1->getPayload()) -// -// OATPP_ASSERT(part2) -// OATPP_ASSERT(part2->getPayload()) -// -// OATPP_ASSERT(part1->getPayload()->getInMemoryData() == "Hello") -// OATPP_ASSERT(part2->getPayload()->getInMemoryData() == "World") -// -// } -// -// { // test interceptor GET -// auto response = client->getInterceptors(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto value = response->readBodyToString(); -// OATPP_ASSERT(value == "Hello World Async!!!") -// } -// -// { // test host header -// auto response = client->getHostHeader(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto value = response->readBodyToString(); -// auto host = clientConnectionProvider->getProperty("host"); -// OATPP_ASSERT(host) -// OATPP_ASSERT(value == host.toString() + ":" + oatpp::utils::Conversion::int32ToStr(m_port)) -// } -// -// if((i + 1) % iterationsStep == 0) { -// auto ticks = oatpp::Environment::getMicroTickCount() - lastTick; -// lastTick = oatpp::Environment::getMicroTickCount(); -// OATPP_LOGv("i", "{}, tick={}", i + 1, ticks) -// } -// -// } -// -// connection.reset(); -// std::this_thread::sleep_for(std::chrono::milliseconds(200)); + OATPP_COMPONENT(std::shared_ptr, clientConnectionProvider); + OATPP_COMPONENT(std::shared_ptr, objectMapper); - std::this_thread::sleep_for(std::chrono::hours (200)); + auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider); + auto client = app::Client::createShared(requestExecutor, objectMapper); + + auto connection = client->getConnection(); + OATPP_ASSERT(connection) + + v_int32 iterationsStep = m_iterationsPerStep; + + auto lastTick = oatpp::Environment::getMicroTickCount(); + + for(v_int32 i = 0; i < iterationsStep * 10; i ++) { + + //OATPP_LOGv("i", "{}", i) + + { // test simple GET + auto response = client->getRoot(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto value = response->readBodyToString(); + OATPP_ASSERT(value == "Hello World Async!!!") + } + + { // test GET with path parameter + auto response = client->getWithParams("my_test_param-Async", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dto = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dto) + OATPP_ASSERT(dto->testValue == "my_test_param-Async") + } + + { // test GET with header parameter + auto response = client->getWithHeaders("my_test_header-Async", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dto = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dto) + OATPP_ASSERT(dto->testValue == "my_test_header-Async") + } + + { // test POST with body + auto response = client->postBody("my_test_body-Async", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dto = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dto) + OATPP_ASSERT(dto->testValue == "my_test_body-Async") + } + + { // test Big Echo with body + oatpp::data::stream::BufferOutputStream stream; + for(v_int32 j = 0; j < oatpp::data::buffer::IOBuffer::BUFFER_SIZE; j++) { + stream.writeSimple("0123456789", 10); + } + auto data = stream.toString(); + auto response = client->echoBody(data, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + + auto returnedData = response->readBodyToString(); + + OATPP_ASSERT(returnedData) + OATPP_ASSERT(returnedData == data) + } + + { // test Chunked body + oatpp::String sample = "__abcdefghijklmnopqrstuvwxyz-0123456789"; + v_int32 numIterations = 10; + oatpp::data::stream::BufferOutputStream stream; + for(v_int32 j = 0; j < numIterations; j++) { + stream.writeSimple(sample->data(), static_cast(sample->size())); + } + auto data = stream.toString(); + auto response = client->getChunked(sample, numIterations, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto returnedData = response->readBodyToString(); + OATPP_ASSERT(returnedData) + OATPP_ASSERT(returnedData == data) + } + + { // Multipart body + + std::unordered_map map; + map["value1"] = "Hello"; + map["value2"] = "World"; + auto multipart = createMultipart(map); + + auto body = std::make_shared(multipart); + + auto response = client->multipartTest(i + 1, body); + OATPP_ASSERT(response->getStatusCode() == 200) + + multipart = std::make_shared(response->getHeaders()); + + oatpp::web::mime::multipart::Reader multipartReader(multipart.get()); + multipartReader.setPartReader("value1", oatpp::web::mime::multipart::createInMemoryPartReader(10)); + multipartReader.setPartReader("value2", oatpp::web::mime::multipart::createInMemoryPartReader(10)); + + response->transferBody(&multipartReader); + + OATPP_ASSERT(multipart->getAllParts().size() == 2) + auto part1 = multipart->getNamedPart("value1"); + auto part2 = multipart->getNamedPart("value2"); + + OATPP_ASSERT(part1) + OATPP_ASSERT(part1->getPayload()) + + OATPP_ASSERT(part2) + OATPP_ASSERT(part2->getPayload()) + + OATPP_ASSERT(part1->getPayload()->getInMemoryData() == "Hello") + OATPP_ASSERT(part2->getPayload()->getInMemoryData() == "World") + + } + + { // test interceptor GET + auto response = client->getInterceptors(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto value = response->readBodyToString(); + OATPP_ASSERT(value == "Hello World Async!!!") + } + + { // test host header + auto response = client->getHostHeader(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto value = response->readBodyToString(); + auto host = clientConnectionProvider->getProperty("host"); + OATPP_ASSERT(host) + OATPP_ASSERT(value == host.toString() + ":" + oatpp::utils::Conversion::int32ToStr(m_port)) + } + + if((i + 1) % iterationsStep == 0) { + auto ticks = oatpp::Environment::getMicroTickCount() - lastTick; + lastTick = oatpp::Environment::getMicroTickCount(); + OATPP_LOGv("i", "{}, tick={}", i + 1, ticks) + } + + } + + connection.reset(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); }, std::chrono::minutes(10)); diff --git a/test/oatpp/web/FullTest.cpp b/test/oatpp/web/FullTest.cpp index b566cdee..0de06b82 100644 --- a/test/oatpp/web/FullTest.cpp +++ b/test/oatpp/web/FullTest.cpp @@ -151,424 +151,422 @@ void FullTest::onRun() { runner.run([this] { -// OATPP_COMPONENT(std::shared_ptr, clientConnectionProvider); -// OATPP_COMPONENT(std::shared_ptr, objectMapper); -// -// auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider); -// auto client = app::Client::createShared(requestExecutor, objectMapper); -// -// auto connection = client->getConnection(); -// OATPP_ASSERT(connection) -// -// v_int32 iterationsStep = m_iterationsPerStep; -// -// auto lastTick = oatpp::Environment::getMicroTickCount(); -// -// for(v_int32 i = 0; i < iterationsStep * 10; i ++) { -// -// { // test simple GET -// auto response = client->getRoot(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto value = response->readBodyToString(); -// OATPP_ASSERT(value == "Hello World!!!") -// } -// -// { // test simple GET with CORS -// auto response = client->getCors(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto value = response->readBodyToString(); -// OATPP_ASSERT(value == "Ping") -// auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "*") -// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "GET, POST, OPTIONS") -// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") -// } -// -// { // test simple OPTIONS with CORS -// auto response = client->optionsCors(connection); -// OATPP_ASSERT(response->getStatusCode() == 204) -// auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "*") -// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "GET, POST, OPTIONS") -// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") -// } -// -// { // test simple GET with CORS -// auto response = client->getCorsOrigin(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto value = response->readBodyToString(); -// OATPP_ASSERT(value == "Pong") -// auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "127.0.0.1") -// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "GET, POST, OPTIONS") -// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") -// } -// -// { // test simple GET with CORS -// auto response = client->getCorsOriginMethods(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto value = response->readBodyToString(); -// OATPP_ASSERT(value == "Ping") -// auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "127.0.0.1") -// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "GET, OPTIONS") -// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") -// } -// -// { // test simple GET with CORS -// auto response = client->getCorsOriginMethodsHeader(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto value = response->readBodyToString(); -// OATPP_ASSERT(value == "Pong") -// auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "127.0.0.1") -// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "GET, OPTIONS") -// header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "X-PWNT") -// } -// -// { // test GET with path parameter -// auto response = client->getWithParams("my_test_param", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dto = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dto) -// OATPP_ASSERT(dto->testValue == "my_test_param") -// } -// -// { // test GET with query parameters -// auto response = client->getWithQueries("oatpp", 1, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dto = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dto) -// OATPP_ASSERT(dto->testValue == "name=oatpp&age=1") -// } -// -// { // test GET with optional query parameters -// auto response = client->getWithOptQueries("oatpp", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dto = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dto) -// OATPP_ASSERT(dto->testValue == "name=oatpp&age=101") -// } -// -// { // test GET with query parameters -// auto response = client->getWithQueriesMap("value1", 32, 0.32f, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dto = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dto) -// OATPP_ASSERT(dto->testMap) -// OATPP_ASSERT(dto->testMap->size() == 3) -// OATPP_ASSERT(dto->testMap["key1"] == "value1") -// OATPP_ASSERT(dto->testMap["key2"] == "32") -// OATPP_ASSERT(dto->testMap["key3"] == oatpp::utils::Conversion::float32ToStr(0.32f)) -// } -// -// { // test GET with header parameter -// auto response = client->getWithHeaders("my_test_header", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dto = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dto) -// OATPP_ASSERT(dto->testValue == "my_test_header") -// } -// -// { // test POST with body -// auto response = client->postBody("my_test_body", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dto = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dto) -// OATPP_ASSERT(dto->testValue == "my_test_body") -// } -// -// { // test POST with dto body -// auto dtoIn = app::TestDto::createShared(); -// dtoIn->testValueInt = i; -// auto response = client->postBodyDto(dtoIn, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dtoOut = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dtoOut) -// OATPP_ASSERT(dtoOut->testValueInt == i) -// } -// -// { // test Enum as String -// -// OATPP_ASSERT(oatpp::Enum::getEntries().size() == 2) -// -// oatpp::Enum v = app::AllowedPathParams::HELLO; -// auto response = client->getHeaderEnumAsString(v, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// } -// -// { // test Enum as String -// oatpp::Enum v = app::AllowedPathParams::HELLO; -// auto response = client->getHeaderEnumAsNumber(v, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// } -// -// { // test Big Echo with body -// oatpp::data::stream::BufferOutputStream stream; -// for(v_int32 j = 0; j < oatpp::data::buffer::IOBuffer::BUFFER_SIZE; j++) { -// stream.writeSimple("0123456789", 10); -// } -// auto data = stream.toString(); -// auto response = client->echoBody(data, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto returnedData = response->readBodyToString(); -// OATPP_ASSERT(returnedData) -// OATPP_ASSERT(returnedData == data) -// } -// -// { -// String bodyIn = "null"; -// auto response = client->testBodyIsNull1(bodyIn, connection); -// OATPP_ASSERT(response->getStatusCode() == 400) -// auto returnedData = response->readBodyToString(); -// OATPP_ASSERT(returnedData) + OATPP_COMPONENT(std::shared_ptr, clientConnectionProvider); + OATPP_COMPONENT(std::shared_ptr, objectMapper); + + auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider); + auto client = app::Client::createShared(requestExecutor, objectMapper); + + auto connection = client->getConnection(); + OATPP_ASSERT(connection) + + v_int32 iterationsStep = m_iterationsPerStep; + + auto lastTick = oatpp::Environment::getMicroTickCount(); + + for(v_int32 i = 0; i < iterationsStep * 10; i ++) { + + { // test simple GET + auto response = client->getRoot(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto value = response->readBodyToString(); + OATPP_ASSERT(value == "Hello World!!!") + } + + { // test simple GET with CORS + auto response = client->getCors(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto value = response->readBodyToString(); + OATPP_ASSERT(value == "Ping") + auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "*") + header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "GET, POST, OPTIONS") + header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") + } + + { // test simple OPTIONS with CORS + auto response = client->optionsCors(connection); + OATPP_ASSERT(response->getStatusCode() == 204) + auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "*") + header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "GET, POST, OPTIONS") + header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") + } + + { // test simple GET with CORS + auto response = client->getCorsOrigin(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto value = response->readBodyToString(); + OATPP_ASSERT(value == "Pong") + auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "127.0.0.1") + header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "GET, POST, OPTIONS") + header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") + } + + { // test simple GET with CORS + auto response = client->getCorsOriginMethods(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto value = response->readBodyToString(); + OATPP_ASSERT(value == "Ping") + auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "127.0.0.1") + header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "GET, OPTIONS") + header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization") + } + + { // test simple GET with CORS + auto response = client->getCorsOriginMethodsHeader(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto value = response->readBodyToString(); + OATPP_ASSERT(value == "Pong") + auto header = response->getHeader(oatpp::web::protocol::http::Header::CORS_ORIGIN); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "127.0.0.1") + header = response->getHeader(oatpp::web::protocol::http::Header::CORS_METHODS); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "GET, OPTIONS") + header = response->getHeader(oatpp::web::protocol::http::Header::CORS_HEADERS); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "X-PWNT") + } + + { // test GET with path parameter + auto response = client->getWithParams("my_test_param", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dto = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dto) + OATPP_ASSERT(dto->testValue == "my_test_param") + } + + { // test GET with query parameters + auto response = client->getWithQueries("oatpp", 1, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dto = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dto) + OATPP_ASSERT(dto->testValue == "name=oatpp&age=1") + } + + { // test GET with optional query parameters + auto response = client->getWithOptQueries("oatpp", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dto = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dto) + OATPP_ASSERT(dto->testValue == "name=oatpp&age=101") + } + + { // test GET with query parameters + auto response = client->getWithQueriesMap("value1", 32, 0.32f, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dto = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dto) + OATPP_ASSERT(dto->testMap) + OATPP_ASSERT(dto->testMap->size() == 3) + OATPP_ASSERT(dto->testMap["key1"] == "value1") + OATPP_ASSERT(dto->testMap["key2"] == "32") + OATPP_ASSERT(dto->testMap["key3"] == oatpp::utils::Conversion::float32ToStr(0.32f)) + } + + { // test GET with header parameter + auto response = client->getWithHeaders("my_test_header", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dto = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dto) + OATPP_ASSERT(dto->testValue == "my_test_header") + } + + { // test POST with body + auto response = client->postBody("my_test_body", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dto = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dto) + OATPP_ASSERT(dto->testValue == "my_test_body") + } + + { // test POST with dto body + auto dtoIn = app::TestDto::createShared(); + dtoIn->testValueInt = i; + auto response = client->postBodyDto(dtoIn, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dtoOut = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dtoOut) + OATPP_ASSERT(dtoOut->testValueInt == i) + } + + { // test Enum as String + + OATPP_ASSERT(oatpp::Enum::getEntries().size() == 2) + + oatpp::Enum v = app::AllowedPathParams::HELLO; + auto response = client->getHeaderEnumAsString(v, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + } + + { // test Enum as String + oatpp::Enum v = app::AllowedPathParams::HELLO; + auto response = client->getHeaderEnumAsNumber(v, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + } + + { // test Big Echo with body + oatpp::data::stream::BufferOutputStream stream; + for(v_int32 j = 0; j < oatpp::data::buffer::IOBuffer::BUFFER_SIZE; j++) { + stream.writeSimple("0123456789", 10); + } + auto data = stream.toString(); + auto response = client->echoBody(data, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto returnedData = response->readBodyToString(); + OATPP_ASSERT(returnedData) + OATPP_ASSERT(returnedData == data) + } + + { + String bodyIn = "null"; + auto response = client->testBodyIsNull1(bodyIn, connection); + OATPP_ASSERT(response->getStatusCode() == 400) + auto returnedData = response->readBodyToString(); + OATPP_ASSERT(returnedData) // OATPP_ASSERT(returnedData == "server=oatpp/" OATPP_VERSION "\n" // "code=400\n" // "description=Bad Request\n" // "message=Missing valid body parameter 'body'\n") -// connection = client->getConnection(); -// } -// -// { -// String bodyIn = "\"null\""; -// auto response = client->testBodyIsNull1(bodyIn, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto returnedData = response->readBodyToString(); -// OATPP_ASSERT(returnedData) -// OATPP_ASSERT(returnedData == "OK---null") -// } -// -// { -// String bodyIn = "null"; -// auto response = client->testBodyIsNull2(bodyIn, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto returnedData = response->readBodyToString(); -// OATPP_ASSERT(returnedData) -// OATPP_ASSERT(returnedData == "OK---null") -// } -// -// { -// String bodyIn = "\"null\""; -// auto response = client->testBodyIsNull2(bodyIn, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto returnedData = response->readBodyToString(); -// OATPP_ASSERT(returnedData) -// OATPP_ASSERT(returnedData == "OK---\"null\"") -// } -// -// { -// auto response = client->headerValueSet(" VALUE_1, VALUE_2, VALUE_3", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// } -// -// { -// auto response = client->getDefaultHeaders1(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// } -// -// { -// auto response = client->getDefaultHeaders2("some param", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// } -// -// { // test custom authorization handler with custom authorization object -// auto response = client->defaultBasicAuthorization("foo:bar", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// } -// -// { // test call of an endpoint that requiers authorization headers, but we don't send one -// auto response = client->defaultBasicAuthorizationWithoutHeader(); -// OATPP_ASSERT(response->getStatusCode() == 401) -// oatpp::String body = response->readBodyToString(); -// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" -// "code=401\n" -// "description=Unauthorized\n" -// "message=Authorization Required\n") -// // should also add the WWW-Authenticate header when Authorization is missing -// auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "Basic realm=\"default-test-realm\"") -// } -// -// { // test custom authorization handler with custom authorization object -// auto response = client->customBasicAuthorization("foo:bar", connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// } -// -// { // test call of an endpoint that requiers authorization headers, but we don't send one -// auto response = client->customBasicAuthorizationWithoutHeader(); -// OATPP_ASSERT(response->getStatusCode() == 401) -// oatpp::String body = response->readBodyToString(); -// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" -// "code=401\n" -// "description=Unauthorized\n" -// "message=Authorization Required\n") -// // should also add the WWW-Authenticate header when Authorization is missing -// auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "Basic realm=\"custom-test-realm\"") -// } -// -// { // test custom authorization handler with custom authorization object with unknown credentials where the -// // handler returns nullptr -// auto response = client->customBasicAuthorization("john:doe"); -// oatpp::String body = response->readBodyToString(); -// OATPP_ASSERT(response->getStatusCode() == 401) -// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" -// "code=401\n" -// "description=Unauthorized\n" -// "message=Unauthorized\n") -// // should also add the WWW-Authenticate header when Authorization is missing or wrong -// auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "Basic realm=\"custom-test-realm\"") -// } -// -// { // test custom authorization handler with custom authorization method -// oatpp::String token = "4e99e8c12de7e01535248d2bac85e732"; -// auto response = client->bearerAuthorization(token); -// oatpp::String body = response->readBodyToString(); -// OATPP_ASSERT(response->getStatusCode() == 200) -// } -// -// { // test custom authorization handler with custom authorization object with unknown credentials where the -// // handler returns nullptr -// oatpp::String token = "900150983cd24fb0d6963f7d28e17f72"; -// auto response = client->bearerAuthorization(token); -// oatpp::String body = response->readBodyToString(); -// OATPP_ASSERT(response->getStatusCode() == 401) -// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" -// "code=401\n" -// "description=Unauthorized\n" -// "message=Unauthorized\n") -// // should also add the WWW-Authenticate header when Authorization is missing or wrong -// auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); -// OATPP_ASSERT(header) -// OATPP_ASSERT(header == "Bearer realm=\"custom-bearer-realm\"") -// } -// -// { // test Chunked body -// oatpp::String sample = "__abcdefghijklmnopqrstuvwxyz-0123456789"; -// v_int32 numIterations = 10; -// oatpp::data::stream::BufferOutputStream stream; -// for(v_int32 j = 0; j < numIterations; j++) { -// stream.writeSimple(sample->data(), static_cast(sample->size())); -// } -// auto data = stream.toString(); -// auto response = client->getChunked(sample, numIterations, connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto returnedData = response->readBodyToString(); -// OATPP_ASSERT(returnedData) -// OATPP_ASSERT(returnedData == data) -// } -// -// { // Multipart body -// -// std::unordered_map map; -// map["value1"] = "Hello"; -// map["value2"] = "World"; -// auto multipart = createMultipart(map); -// -// auto body = std::make_shared(multipart); -// -// auto response = client->multipartTest(i + 1, body); -// OATPP_ASSERT(response->getStatusCode() == 200) -// -// multipart = std::make_shared(response->getHeaders()); -// -// oatpp::web::mime::multipart::Reader multipartReader(multipart.get()); -// multipartReader.setPartReader("value1", oatpp::web::mime::multipart::createInMemoryPartReader(10)); -// multipartReader.setPartReader("value2", oatpp::web::mime::multipart::createInMemoryPartReader(10)); -// -// response->transferBody(&multipartReader); -// -// OATPP_ASSERT(multipart->getAllParts().size() == 2) -// auto part1 = multipart->getNamedPart("value1"); -// auto part2 = multipart->getNamedPart("value2"); -// -// OATPP_ASSERT(part1) -// OATPP_ASSERT(part1->getPayload()) -// -// OATPP_ASSERT(part2) -// OATPP_ASSERT(part2->getPayload()) -// -// OATPP_ASSERT(part1->getPayload()->getInMemoryData() == "Hello") -// OATPP_ASSERT(part2->getPayload()->getInMemoryData() == "World") -// -// } -// -// { // test interceptors -// auto response = client->getInterceptors(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto value = response->readBodyToString(); -// OATPP_ASSERT(value == "Hello World!!!") -// } -// -// { // test controller's error handler catches -// auto response = client->getCaughtError(connection); -// OATPP_ASSERT(response->getStatusCode() == 418) -// auto value = response->readBodyToString(); -// OATPP_ASSERT(value == "Controller With Errors!") -// } -// -// { // test header replacement -// auto response = client->getInterceptors(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// OATPP_ASSERT(response->getHeader("to-be-replaced") == "replaced_value") -// } -// -// if((i + 1) % iterationsStep == 0) { -// auto ticks = oatpp::Environment::getMicroTickCount() - lastTick; -// lastTick = oatpp::Environment::getMicroTickCount(); -// OATPP_LOGv("i", "{}, tick={}", i + 1, ticks) -// } -// -// { // test bundle -// auto response = client->getBundle(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto dto = response->readBodyToDto>(objectMapper.get()); -// OATPP_ASSERT(dto) -// OATPP_ASSERT(dto->testValue == "str-param") -// OATPP_ASSERT(dto->testValueInt == 32000) -// } -// -// { // test host header -// auto response = client->getHostHeader(connection); -// OATPP_ASSERT(response->getStatusCode() == 200) -// auto value = response->readBodyToString(); -// auto host = clientConnectionProvider->getProperty("host"); -// OATPP_ASSERT(host) -// OATPP_ASSERT(value == host.toString() + ":" + oatpp::utils::Conversion::int32ToStr(m_port)) -// } -// -// } + connection = client->getConnection(); + } - std::this_thread::sleep_for(std::chrono::hours (1)); + { + String bodyIn = "\"null\""; + auto response = client->testBodyIsNull1(bodyIn, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto returnedData = response->readBodyToString(); + OATPP_ASSERT(returnedData) + OATPP_ASSERT(returnedData == "OK---null") + } + + { + String bodyIn = "null"; + auto response = client->testBodyIsNull2(bodyIn, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto returnedData = response->readBodyToString(); + OATPP_ASSERT(returnedData) + OATPP_ASSERT(returnedData == "OK---null") + } + + { + String bodyIn = "\"null\""; + auto response = client->testBodyIsNull2(bodyIn, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto returnedData = response->readBodyToString(); + OATPP_ASSERT(returnedData) + OATPP_ASSERT(returnedData == "OK---\"null\"") + } + + { + auto response = client->headerValueSet(" VALUE_1, VALUE_2, VALUE_3", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + } + + { + auto response = client->getDefaultHeaders1(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + } + + { + auto response = client->getDefaultHeaders2("some param", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + } + + { // test custom authorization handler with custom authorization object + auto response = client->defaultBasicAuthorization("foo:bar", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + } + + { // test call of an endpoint that requiers authorization headers, but we don't send one + auto response = client->defaultBasicAuthorizationWithoutHeader(); + OATPP_ASSERT(response->getStatusCode() == 401) + oatpp::String body = response->readBodyToString(); +// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" +// "code=401\n" +// "description=Unauthorized\n" +// "message=Authorization Required\n") + // should also add the WWW-Authenticate header when Authorization is missing + auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "Basic realm=\"default-test-realm\"") + } + + { // test custom authorization handler with custom authorization object + auto response = client->customBasicAuthorization("foo:bar", connection); + OATPP_ASSERT(response->getStatusCode() == 200) + } + + { // test call of an endpoint that requiers authorization headers, but we don't send one + auto response = client->customBasicAuthorizationWithoutHeader(); + OATPP_ASSERT(response->getStatusCode() == 401) + oatpp::String body = response->readBodyToString(); +// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" +// "code=401\n" +// "description=Unauthorized\n" +// "message=Authorization Required\n") + // should also add the WWW-Authenticate header when Authorization is missing + auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "Basic realm=\"custom-test-realm\"") + } + + { // test custom authorization handler with custom authorization object with unknown credentials where the + // handler returns nullptr + auto response = client->customBasicAuthorization("john:doe"); + oatpp::String body = response->readBodyToString(); + OATPP_ASSERT(response->getStatusCode() == 401) +// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" +// "code=401\n" +// "description=Unauthorized\n" +// "message=Unauthorized\n") + // should also add the WWW-Authenticate header when Authorization is missing or wrong + auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "Basic realm=\"custom-test-realm\"") + } + + { // test custom authorization handler with custom authorization method + oatpp::String token = "4e99e8c12de7e01535248d2bac85e732"; + auto response = client->bearerAuthorization(token); + oatpp::String body = response->readBodyToString(); + OATPP_ASSERT(response->getStatusCode() == 200) + } + + { // test custom authorization handler with custom authorization object with unknown credentials where the + // handler returns nullptr + oatpp::String token = "900150983cd24fb0d6963f7d28e17f72"; + auto response = client->bearerAuthorization(token); + oatpp::String body = response->readBodyToString(); + OATPP_ASSERT(response->getStatusCode() == 401) +// OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n" +// "code=401\n" +// "description=Unauthorized\n" +// "message=Unauthorized\n") + // should also add the WWW-Authenticate header when Authorization is missing or wrong + auto header = response->getHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE); + OATPP_ASSERT(header) + OATPP_ASSERT(header == "Bearer realm=\"custom-bearer-realm\"") + } + + { // test Chunked body + oatpp::String sample = "__abcdefghijklmnopqrstuvwxyz-0123456789"; + v_int32 numIterations = 10; + oatpp::data::stream::BufferOutputStream stream; + for(v_int32 j = 0; j < numIterations; j++) { + stream.writeSimple(sample->data(), static_cast(sample->size())); + } + auto data = stream.toString(); + auto response = client->getChunked(sample, numIterations, connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto returnedData = response->readBodyToString(); + OATPP_ASSERT(returnedData) + OATPP_ASSERT(returnedData == data) + } + + { // Multipart body + + std::unordered_map map; + map["value1"] = "Hello"; + map["value2"] = "World"; + auto multipart = createMultipart(map); + + auto body = std::make_shared(multipart); + + auto response = client->multipartTest(i + 1, body); + OATPP_ASSERT(response->getStatusCode() == 200) + + multipart = std::make_shared(response->getHeaders()); + + oatpp::web::mime::multipart::Reader multipartReader(multipart.get()); + multipartReader.setPartReader("value1", oatpp::web::mime::multipart::createInMemoryPartReader(10)); + multipartReader.setPartReader("value2", oatpp::web::mime::multipart::createInMemoryPartReader(10)); + + response->transferBody(&multipartReader); + + OATPP_ASSERT(multipart->getAllParts().size() == 2) + auto part1 = multipart->getNamedPart("value1"); + auto part2 = multipart->getNamedPart("value2"); + + OATPP_ASSERT(part1) + OATPP_ASSERT(part1->getPayload()) + + OATPP_ASSERT(part2) + OATPP_ASSERT(part2->getPayload()) + + OATPP_ASSERT(part1->getPayload()->getInMemoryData() == "Hello") + OATPP_ASSERT(part2->getPayload()->getInMemoryData() == "World") + + } + + { // test interceptors + auto response = client->getInterceptors(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto value = response->readBodyToString(); + OATPP_ASSERT(value == "Hello World!!!") + } + + { // test controller's error handler catches + auto response = client->getCaughtError(connection); + OATPP_ASSERT(response->getStatusCode() == 418) + auto value = response->readBodyToString(); + OATPP_ASSERT(value == "Controller With Errors!") + } + + { // test header replacement + auto response = client->getInterceptors(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + OATPP_ASSERT(response->getHeader("to-be-replaced") == "replaced_value") + } + + if((i + 1) % iterationsStep == 0) { + auto ticks = oatpp::Environment::getMicroTickCount() - lastTick; + lastTick = oatpp::Environment::getMicroTickCount(); + OATPP_LOGv("i", "{}, tick={}", i + 1, ticks) + } + + { // test bundle + auto response = client->getBundle(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto dto = response->readBodyToDto>(objectMapper.get()); + OATPP_ASSERT(dto) + OATPP_ASSERT(dto->testValue == "str-param") + OATPP_ASSERT(dto->testValueInt == 32000) + } + + { // test host header + auto response = client->getHostHeader(connection); + OATPP_ASSERT(response->getStatusCode() == 200) + auto value = response->readBodyToString(); + auto host = clientConnectionProvider->getProperty("host"); + OATPP_ASSERT(host) + OATPP_ASSERT(value == host.toString() + ":" + oatpp::utils::Conversion::int32ToStr(m_port)) + } + + } }, std::chrono::minutes(10)); diff --git a/test/oatpp/web/app/ControllerAsync.hpp b/test/oatpp/web/app/ControllerAsync.hpp index 1f7e3de7..2f935085 100644 --- a/test/oatpp/web/app/ControllerAsync.hpp +++ b/test/oatpp/web/app/ControllerAsync.hpp @@ -95,6 +95,7 @@ public: Action act() override { auto param = request->getHeader("X-TEST-HEADER"); + OATPP_ASSERT_HTTP(param, Status::CODE_400, "X-TEST-HEADER missing") //OATPP_LOGv(TAG, "GET headers {X-TEST-HEADER: {}}", param) auto dto = TestDto::createShared(); dto->testValue = param; diff --git a/test/oatpp/web/app/ControllerWithErrorHandler.hpp b/test/oatpp/web/app/ControllerWithErrorHandler.hpp index 53c56e72..e538009b 100644 --- a/test/oatpp/web/app/ControllerWithErrorHandler.hpp +++ b/test/oatpp/web/app/ControllerWithErrorHandler.hpp @@ -37,33 +37,13 @@ namespace oatpp { namespace test { namespace web { namespace app { namespace http = oatpp::web::protocol::http; -/** - * Custom Error Handler. - */ -class CustomErrorHandler : public oatpp::base::Countable, public oatpp::web::server::handler::ErrorHandler { +class CustomErrorHandler : public oatpp::web::server::handler::DefaultErrorHandler { public: - /** - * Constructor. - */ + CustomErrorHandler() = default; -public: - /** - * Create shared DefaultErrorHandler. - * @return - `std::shared_ptr` to DefaultErrorHandler. - */ - static std::shared_ptr createShared() { - return std::make_shared(); - } - - std::shared_ptr handleError(const std::exception_ptr& error) override { - try { - std::rethrow_exception(error); - } catch(const std::runtime_error& e) { - return oatpp::web::protocol::http::outgoing::ResponseFactory::createResponse(http::Status::CODE_418, e.what()); - } catch(...) { - throw; - } + std::shared_ptr renderError(const HttpServerErrorStacktrace& stacktrace) override { + return oatpp::web::protocol::http::outgoing::ResponseFactory::createResponse(http::Status::CODE_418, stacktrace.stack.front()); } }; @@ -75,7 +55,7 @@ public: explicit ControllerWithErrorHandler(const std::shared_ptr& objectMapper) : oatpp::web::server::api::ApiController(objectMapper) { - setErrorHandler(CustomErrorHandler::createShared()); + setErrorHandler(std::make_shared()); } public: From 35a43ed39d57c2ef23bc54eee1340c9314f6284d Mon Sep 17 00:00:00 2001 From: Leonid Stryzhevskyi Date: Tue, 4 Jun 2024 02:36:31 +0300 Subject: [PATCH 3/4] tests: uncomment all --- test/oatpp/AllTestsMain.cpp | 289 ++++++++++++++++++------------------ 1 file changed, 143 insertions(+), 146 deletions(-) diff --git a/test/oatpp/AllTestsMain.cpp b/test/oatpp/AllTestsMain.cpp index 1d68c6d9..6378f3e9 100644 --- a/test/oatpp/AllTestsMain.cpp +++ b/test/oatpp/AllTestsMain.cpp @@ -84,136 +84,133 @@ namespace { void runTests() { -// oatpp::Environment::printCompilationConfig(); -// -// OATPP_LOGd("Tests", "oatpp::String size={}", sizeof(oatpp::String)) -// -// OATPP_LOGd("Tests", "oatpp::String size={}", sizeof(oatpp::String)) -// OATPP_LOGd("Tests", "std::string size={}", sizeof(std::string)) -// OATPP_LOGd("Tests", "Vector size={}", sizeof(std::vector)) -// OATPP_LOGd("Tests", "Map size={}", sizeof(std::unordered_map)) -// OATPP_LOGd("Tests", "Tree size={}", sizeof(oatpp::data::mapping::Tree)) -// -// //return; -// + oatpp::Environment::printCompilationConfig(); + + OATPP_LOGd("Tests", "oatpp::String size={}", sizeof(oatpp::String)) + + OATPP_LOGd("Tests", "oatpp::String size={}", sizeof(oatpp::String)) + OATPP_LOGd("Tests", "std::string size={}", sizeof(std::string)) + OATPP_LOGd("Tests", "Vector size={}", sizeof(std::vector)) + OATPP_LOGd("Tests", "Map size={}", sizeof(std::unordered_map)) + OATPP_LOGd("Tests", "Tree size={}", sizeof(oatpp::data::mapping::Tree)) + OATPP_LOGd("Tests", "coroutine handle size={}", sizeof(oatpp::async::CoroutineHandle)) OATPP_LOGd("Tests", "coroutine size={}", sizeof(oatpp::async::AbstractCoroutine)) OATPP_LOGd("Tests", "action size={}", sizeof(oatpp::async::Action)) - OATPP_LOGd("Tests", "exception size={}", sizeof(std::exception_ptr)) OATPP_LOGd("Tests", "class count={}", oatpp::data::type::ClassId::getClassCount()) -// -// auto names = oatpp::data::type::ClassId::getRegisteredClassNames(); -// v_int32 i = 0; -// for(auto& name : names) { -// OATPP_LOGd("CLASS", "{} --> '{}'", i, name) -// i ++; -// } -// -// OATPP_RUN_TEST(oatpp::test::LoggerTest); -// OATPP_RUN_TEST(oatpp::base::CommandLineArgumentsTest); -// OATPP_RUN_TEST(oatpp::base::LogTest); -// -// OATPP_RUN_TEST(oatpp::data::share::MemoryLabelTest); -// OATPP_RUN_TEST(oatpp::data::share::LazyStringMapTest); -// OATPP_RUN_TEST(oatpp::data::share::StringTemplateTest); -// -// OATPP_RUN_TEST(oatpp::data::buffer::ProcessorTest); -// OATPP_RUN_TEST(oatpp::data::stream::BufferStreamTest); -// -// OATPP_RUN_TEST(oatpp::data::mapping::TreeTest); -// OATPP_RUN_TEST(oatpp::data::mapping::ObjectToTreeMapperTest); -// OATPP_RUN_TEST(oatpp::data::mapping::TreeToObjectMapperTest); -// OATPP_RUN_TEST(oatpp::data::mapping::ObjectRemapperTest); -// -// OATPP_RUN_TEST(oatpp::data::type::ObjectWrapperTest); -// OATPP_RUN_TEST(oatpp::data::type::TypeTest); -// -// OATPP_RUN_TEST(oatpp::data::type::StringTest); -// -// OATPP_RUN_TEST(oatpp::data::type::PrimitiveTest); -// OATPP_RUN_TEST(oatpp::data::type::ListTest); -// OATPP_RUN_TEST(oatpp::data::type::VectorTest); -// OATPP_RUN_TEST(oatpp::data::type::UnorderedSetTest); -// OATPP_RUN_TEST(oatpp::data::type::PairListTest); -// OATPP_RUN_TEST(oatpp::data::type::UnorderedMapTest); -// OATPP_RUN_TEST(oatpp::data::type::AnyTest); -// OATPP_RUN_TEST(oatpp::data::type::EnumTest); -// -// OATPP_RUN_TEST(oatpp::data::type::ObjectTest); -// -// OATPP_RUN_TEST(oatpp::data::type::InterpretationTest); -// OATPP_RUN_TEST(oatpp::data::mapping::TypeResolverTest); -// -// OATPP_RUN_TEST(oatpp::data::resource::InMemoryDataTest); -// -// OATPP_RUN_TEST(oatpp::async::ConditionVariableTest); -// OATPP_RUN_TEST(oatpp::async::LockTest); -// -// OATPP_RUN_TEST(oatpp::utils::parser::CaretTest); -// -// OATPP_RUN_TEST(oatpp::provider::PoolTest); -// OATPP_RUN_TEST(oatpp::provider::PoolTemplateTest); -// -// OATPP_RUN_TEST(oatpp::json::EnumTest); -// OATPP_RUN_TEST(oatpp::json::BooleanTest); -// -// OATPP_RUN_TEST(oatpp::json::UnorderedSetTest); -// -// OATPP_RUN_TEST(oatpp::json::DeserializerTest); -// -// OATPP_RUN_TEST(oatpp::json::DTOMapperPerfTest); -// -// OATPP_RUN_TEST(oatpp::json::DTOMapperTest); -// OATPP_RUN_TEST(oatpp::test::encoding::Base64Test); -// OATPP_RUN_TEST(oatpp::encoding::HexTest); -// OATPP_RUN_TEST(oatpp::test::encoding::UnicodeTest); -// OATPP_RUN_TEST(oatpp::test::encoding::UrlTest); -// -// OATPP_RUN_TEST(oatpp::test::network::UrlTest); -// OATPP_RUN_TEST(oatpp::test::network::ConnectionPoolTest); -// OATPP_RUN_TEST(oatpp::test::network::monitor::ConnectionMonitorTest); -// OATPP_RUN_TEST(oatpp::test::network::virtual_::PipeTest); -// OATPP_RUN_TEST(oatpp::test::network::virtual_::InterfaceTest); -// -// OATPP_RUN_TEST(oatpp::test::web::protocol::http::encoding::ChunkedTest); -// -// OATPP_RUN_TEST(oatpp::test::web::mime::multipart::StatefulParserTest); -// OATPP_RUN_TEST(oatpp::web::mime::ContentMappersTest); -// -// OATPP_RUN_TEST(oatpp::test::web::server::HttpRouterTest); -// OATPP_RUN_TEST(oatpp::test::web::server::api::ApiControllerTest); -// OATPP_RUN_TEST(oatpp::test::web::server::handler::AuthorizationHandlerTest); -// -// { -// -// oatpp::test::web::server::ServerStopTest test_virtual(0); -// test_virtual.run(); -// -// oatpp::test::web::server::ServerStopTest test_port(8000); -// test_port.run(); -// -// } -// -// { -// -// oatpp::test::web::PipelineTest test_virtual(0, 3000); -// test_virtual.run(); -// -// oatpp::test::web::PipelineTest test_port(8000, 3000); -// test_port.run(); -// -// } -// -// { -// -// oatpp::test::web::PipelineAsyncTest test_virtual(0, 3000); -// test_virtual.run(); -// -// oatpp::test::web::PipelineAsyncTest test_port(8000, 3000); -// test_port.run(); -// -// } -// + + auto names = oatpp::data::type::ClassId::getRegisteredClassNames(); + v_int32 i = 0; + for(auto& name : names) { + OATPP_LOGd("CLASS", "{} --> '{}'", i, name) + i ++; + } + + OATPP_RUN_TEST(oatpp::test::LoggerTest); + OATPP_RUN_TEST(oatpp::base::CommandLineArgumentsTest); + OATPP_RUN_TEST(oatpp::base::LogTest); + + OATPP_RUN_TEST(oatpp::data::share::MemoryLabelTest); + OATPP_RUN_TEST(oatpp::data::share::LazyStringMapTest); + OATPP_RUN_TEST(oatpp::data::share::StringTemplateTest); + + OATPP_RUN_TEST(oatpp::data::buffer::ProcessorTest); + OATPP_RUN_TEST(oatpp::data::stream::BufferStreamTest); + + OATPP_RUN_TEST(oatpp::data::mapping::TreeTest); + OATPP_RUN_TEST(oatpp::data::mapping::ObjectToTreeMapperTest); + OATPP_RUN_TEST(oatpp::data::mapping::TreeToObjectMapperTest); + OATPP_RUN_TEST(oatpp::data::mapping::ObjectRemapperTest); + + OATPP_RUN_TEST(oatpp::data::type::ObjectWrapperTest); + OATPP_RUN_TEST(oatpp::data::type::TypeTest); + + OATPP_RUN_TEST(oatpp::data::type::StringTest); + + OATPP_RUN_TEST(oatpp::data::type::PrimitiveTest); + OATPP_RUN_TEST(oatpp::data::type::ListTest); + OATPP_RUN_TEST(oatpp::data::type::VectorTest); + OATPP_RUN_TEST(oatpp::data::type::UnorderedSetTest); + OATPP_RUN_TEST(oatpp::data::type::PairListTest); + OATPP_RUN_TEST(oatpp::data::type::UnorderedMapTest); + OATPP_RUN_TEST(oatpp::data::type::AnyTest); + OATPP_RUN_TEST(oatpp::data::type::EnumTest); + + OATPP_RUN_TEST(oatpp::data::type::ObjectTest); + + OATPP_RUN_TEST(oatpp::data::type::InterpretationTest); + OATPP_RUN_TEST(oatpp::data::mapping::TypeResolverTest); + + OATPP_RUN_TEST(oatpp::data::resource::InMemoryDataTest); + + OATPP_RUN_TEST(oatpp::async::ConditionVariableTest); + OATPP_RUN_TEST(oatpp::async::LockTest); + + OATPP_RUN_TEST(oatpp::utils::parser::CaretTest); + + OATPP_RUN_TEST(oatpp::provider::PoolTest); + OATPP_RUN_TEST(oatpp::provider::PoolTemplateTest); + + OATPP_RUN_TEST(oatpp::json::EnumTest); + OATPP_RUN_TEST(oatpp::json::BooleanTest); + + OATPP_RUN_TEST(oatpp::json::UnorderedSetTest); + + OATPP_RUN_TEST(oatpp::json::DeserializerTest); + + OATPP_RUN_TEST(oatpp::json::DTOMapperPerfTest); + + OATPP_RUN_TEST(oatpp::json::DTOMapperTest); + OATPP_RUN_TEST(oatpp::test::encoding::Base64Test); + OATPP_RUN_TEST(oatpp::encoding::HexTest); + OATPP_RUN_TEST(oatpp::test::encoding::UnicodeTest); + OATPP_RUN_TEST(oatpp::test::encoding::UrlTest); + + OATPP_RUN_TEST(oatpp::test::network::UrlTest); + OATPP_RUN_TEST(oatpp::test::network::ConnectionPoolTest); + OATPP_RUN_TEST(oatpp::test::network::monitor::ConnectionMonitorTest); + OATPP_RUN_TEST(oatpp::test::network::virtual_::PipeTest); + OATPP_RUN_TEST(oatpp::test::network::virtual_::InterfaceTest); + + OATPP_RUN_TEST(oatpp::test::web::protocol::http::encoding::ChunkedTest); + + OATPP_RUN_TEST(oatpp::test::web::mime::multipart::StatefulParserTest); + OATPP_RUN_TEST(oatpp::web::mime::ContentMappersTest); + + OATPP_RUN_TEST(oatpp::test::web::server::HttpRouterTest); + OATPP_RUN_TEST(oatpp::test::web::server::api::ApiControllerTest); + OATPP_RUN_TEST(oatpp::test::web::server::handler::AuthorizationHandlerTest); + + { + + oatpp::test::web::server::ServerStopTest test_virtual(0); + test_virtual.run(); + + oatpp::test::web::server::ServerStopTest test_port(8000); + test_port.run(); + + } + + { + + oatpp::test::web::PipelineTest test_virtual(0, 3000); + test_virtual.run(); + + oatpp::test::web::PipelineTest test_port(8000, 3000); + test_port.run(); + + } + + { + + oatpp::test::web::PipelineAsyncTest test_virtual(0, 3000); + test_virtual.run(); + + oatpp::test::web::PipelineAsyncTest test_port(8000, 3000); + test_port.run(); + + } + { oatpp::test::web::FullTest test_virtual(0, 1000); @@ -233,26 +230,26 @@ void runTests() { test_port.run(); } -// -// { -// -// oatpp::test::web::FullAsyncClientTest test_virtual(0, 1000); -// test_virtual.run(20); -// -// oatpp::test::web::FullAsyncClientTest test_port(8000, 5); -// test_port.run(1); -// -// } -// -// { -// -// oatpp::test::web::ClientRetryTest test_virtual(0); -// test_virtual.run(); -// -// oatpp::test::web::ClientRetryTest test_port(8000); -// test_port.run(); -// -// } + + { + + oatpp::test::web::FullAsyncClientTest test_virtual(0, 1000); + test_virtual.run(20); + + oatpp::test::web::FullAsyncClientTest test_port(8000, 5); + test_port.run(1); + + } + + { + + oatpp::test::web::ClientRetryTest test_virtual(0); + test_virtual.run(); + + oatpp::test::web::ClientRetryTest test_port(8000); + test_port.run(); + + } } From 9ba892ec50acf724d0e0f1125aefe5b248e0759b Mon Sep 17 00:00:00 2001 From: Leonid Stryzhevskyi Date: Tue, 4 Jun 2024 03:36:17 +0300 Subject: [PATCH 4/4] changelog/1.4.0: add 'Better error handling' section. --- changelog/1.4.0.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/changelog/1.4.0.md b/changelog/1.4.0.md index 3928a71a..1602fa45 100644 --- a/changelog/1.4.0.md +++ b/changelog/1.4.0.md @@ -13,6 +13,7 @@ Contents: - [Remapper](#remapper) - [oatpp::web::mime::ContentMappers](#oatppwebmimecontentmappers) - [New OATPP_LOGx format](#new-oatpp_logx-format) +- [Better error handling](#better-error-handling) - [Restructuring](#restructuring) @@ -172,6 +173,66 @@ Instead of old formatting "%s", "%d", "%f" use "{}" for any variable type: OATPP_LOGd("MyController", "User: name={}, age={}", user->name, user->age) ``` +## Better error handling + +Fixed error handling in the `ApiController` and `ConnectionHandler`. + +The new recommended way to implement custom error handler: + +```cpp +/** + * Extend the DefaultErrorHandling class by overriding the renderError() method instead of the handleError() method. + * You can still override the handleError() method, but in most cases, it isn't necessary. + */ +class ErrorHandler : public oatpp::web::server::handler::DefaultErrorHandler { +private: + /* mappers to map response according to 'Accept' header and available mappers */ + std::shared_ptr m_mappers; +public: + + ErrorHandler(const std::shared_ptr& mappers) + : m_mappers(mappers) + {} + + std::shared_ptr renderError(const HttpServerErrorStacktrace& stacktrace) override { + + /* create ErrorDto */ + auto error = ErrorDto::createShared(); + error->code = stacktrace.status.code; + error->stack = {}; // initialize stack as empty list + + /* push all items of stacktrace */ + for(auto& s : stacktrace.stack) { + error->stack->push_back(s); + } + + /* get all variants of acceptable mime-types from request */ + std::vector acceptable; + if(stacktrace.request) { + acceptable = stacktrace.request->getHeaderValues("Accept"); + } + + /* select mapper based on provided preferences */ + auto mapper = m_mappers->selectMapper(acceptable); + if(!mapper) { + mapper = m_mappers->getDefaultMapper(); + } + + /* create response object */ + auto response = ResponseFactory::createResponse(stacktrace.status, error, mapper); + + /* put all error related headers */ + for(const auto& pair : stacktrace.headers.getAll()) { + response->putHeader(pair.first.toString(), pair.second.toString()); + } + + /* return response */ + return response; + + } + +}; +``` ## Restructuring