From f22afbe89cbe290e8399ab730d1635b498f92681 Mon Sep 17 00:00:00 2001 From: Leonid Stryzhevskyi Date: Tue, 4 Jun 2024 01:39:10 +0300 Subject: [PATCH] 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: