From bd9d290b82a7e177892c7652cc6253f782a753e3 Mon Sep 17 00:00:00 2001 From: Nitromelon Date: Fri, 19 Aug 2022 09:24:02 +0800 Subject: [PATCH] Support coroutine filter. (#1352) --- lib/inc/drogon/HttpAppFramework.h | 3 +- lib/inc/drogon/HttpFilter.h | 67 +++++++++++++++++-- lib/tests/CMakeLists.txt | 1 + lib/tests/integration_test/client/main.cc | 19 ++++++ .../integration_test/server/CoroFilter.cpp | 12 ++++ .../integration_test/server/CoroFilter.h | 17 +++++ .../integration_test/server/api_v1_CoroTest.h | 1 + 7 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 lib/tests/integration_test/server/CoroFilter.cpp create mode 100644 lib/tests/integration_test/server/CoroFilter.h diff --git a/lib/inc/drogon/HttpAppFramework.h b/lib/inc/drogon/HttpAppFramework.h index 6ea2e56b..dbe7c818 100644 --- a/lib/inc/drogon/HttpAppFramework.h +++ b/lib/inc/drogon/HttpAppFramework.h @@ -19,8 +19,7 @@ #include #include #include -#include -#include +#include #include #include #include diff --git a/lib/inc/drogon/HttpFilter.h b/lib/inc/drogon/HttpFilter.h index 45a4e632..5abbeafb 100644 --- a/lib/inc/drogon/HttpFilter.h +++ b/lib/inc/drogon/HttpFilter.h @@ -20,6 +20,10 @@ #include #include +#ifdef __cpp_impl_coroutine +#include +#endif + namespace drogon { /** @@ -43,9 +47,7 @@ class DROGON_EXPORT HttpFilterBase : public virtual DrObjectBase virtual void doFilter(const HttpRequestPtr &req, FilterCallback &&fcb, FilterChainCallback &&fccb) = 0; - virtual ~HttpFilterBase() - { - } + ~HttpFilterBase() override = default; }; /** @@ -60,8 +62,61 @@ class HttpFilter : public DrObject, public HttpFilterBase { public: static constexpr bool isAutoCreation{AutoCreation}; - virtual ~HttpFilter() - { - } + ~HttpFilter() override = default; }; + +namespace internal +{ +DROGON_EXPORT void handleException( + const std::exception &, + const HttpRequestPtr &, + std::function &&); +} + +#ifdef __cpp_impl_coroutine +template +class HttpCoroFilter : public DrObject, public HttpFilterBase +{ + public: + static constexpr bool isAutoCreation{AutoCreation}; + ~HttpCoroFilter() override = default; + void doFilter(const HttpRequestPtr &req, + FilterCallback &&fcb, + FilterChainCallback &&fccb) final + { + drogon::async_run([this, + req, + fcb = std::move(fcb), + fccb = std::move(fccb)]() mutable -> drogon::Task<> { + HttpResponsePtr resp; + try + { + resp = co_await doFilter(req); + } + catch (const std::exception &ex) + { + internal::handleException(ex, req, std::move(fcb)); + co_return; + } + catch (...) + { + LOG_ERROR << "Exception not derived from std::exception"; + co_return; + } + + if (resp) + { + fcb(resp); + } + else + { + fccb(); + } + }); + } + + virtual Task doFilter(const HttpRequestPtr &req) = 0; +}; +#endif + } // namespace drogon diff --git a/lib/tests/CMakeLists.txt b/lib/tests/CMakeLists.txt index 05e02164..2578a4de 100644 --- a/lib/tests/CMakeLists.txt +++ b/lib/tests/CMakeLists.txt @@ -72,6 +72,7 @@ if (BUILD_CTL) if(DROGON_CXX_STANDARD GREATER_EQUAL 20 AND HAS_COROUTINE) set(INTEGRATION_TEST_SERVER_SOURCES ${INTEGRATION_TEST_SERVER_SOURCES} + integration_test/server/CoroFilter.cpp integration_test/server/api_v1_CoroTest.cc) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) diff --git a/lib/tests/integration_test/client/main.cc b/lib/tests/integration_test/client/main.cc index 4a73f0d5..d8396a4d 100644 --- a/lib/tests/integration_test/client/main.cc +++ b/lib/tests/integration_test/client/main.cc @@ -24,6 +24,7 @@ #include #include #include +#include #ifndef _WIN32 #include @@ -1011,6 +1012,7 @@ void doTest(const HttpClientPtr &client, std::shared_ptr TEST_CTX) CHECK(resp->getStatusCode() == k200OK); CHECK(resp->body() == largeString); }); + #ifdef USE_BROTLI // Post compressed data req = HttpRequest::newHttpRequest(); @@ -1093,6 +1095,23 @@ void doTest(const HttpClientPtr &client, std::shared_ptr TEST_CTX) { FAIL("Unexpected exception, what()" + std::string(e.what())); } + + // Test coroutine filter + try + { + auto req = HttpRequest::newHttpRequest(); + auto start = std::chrono::system_clock::now(); + req->setPath("/api/v1/corotest/delay?secs=2"); + auto resp = co_await client->sendRequestCoro(req); + CHECK(resp->getStatusCode() == k200OK); + auto end = std::chrono::system_clock::now(); + std::chrono::duration duration = end - start; + CHECK(duration.count() >= 2000); + } + catch (const std::exception &e) + { + FAIL("Unexpected exception, what()" + std::string(e.what())); + } }()); #endif } diff --git a/lib/tests/integration_test/server/CoroFilter.cpp b/lib/tests/integration_test/server/CoroFilter.cpp new file mode 100644 index 00000000..bbb9be76 --- /dev/null +++ b/lib/tests/integration_test/server/CoroFilter.cpp @@ -0,0 +1,12 @@ +// +// Created by wanchen.he on 2022/8/16. +// + +#include "CoroFilter.h" + +Task CoroFilter::doFilter(const HttpRequestPtr& req) +{ + int secs = std::stoi(req->getParameter("secs")); + co_await sleepCoro(trantor::EventLoop::getEventLoopOfCurrentThread(), secs); + co_return {}; +} diff --git a/lib/tests/integration_test/server/CoroFilter.h b/lib/tests/integration_test/server/CoroFilter.h new file mode 100644 index 00000000..0f0d61e5 --- /dev/null +++ b/lib/tests/integration_test/server/CoroFilter.h @@ -0,0 +1,17 @@ +// +// Created by wanchen.he on 2022/8/16. +// +#pragma once + +#include +using namespace drogon; + +class CoroFilter : public drogon::HttpCoroFilter +{ + public: + Task doFilter(const HttpRequestPtr &req) override; + CoroFilter() + { + LOG_DEBUG << "CoroFilter constructor"; + } +}; diff --git a/lib/tests/integration_test/server/api_v1_CoroTest.h b/lib/tests/integration_test/server/api_v1_CoroTest.h index ed05f0f6..b66cfd0d 100644 --- a/lib/tests/integration_test/server/api_v1_CoroTest.h +++ b/lib/tests/integration_test/server/api_v1_CoroTest.h @@ -11,6 +11,7 @@ class CoroTest : public drogon::HttpController METHOD_LIST_BEGIN METHOD_ADD(CoroTest::get, "/get", Get); METHOD_ADD(CoroTest::get2, "/get2", Get); + METHOD_ADD(CoroTest::get2, "/delay", Get, "CoroFilter"); METHOD_ADD(CoroTest::this_will_fail, "/this_will_fail", Get); METHOD_ADD(CoroTest::this_will_fail2, "/this_will_fail2", Get); METHOD_LIST_END