Support coroutine filter. (#1352)

This commit is contained in:
Nitromelon 2022-08-19 09:24:02 +08:00 committed by GitHub
parent 4ef31d7c2d
commit bd9d290b82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 112 additions and 8 deletions

View File

@ -19,8 +19,7 @@
#include <drogon/CacheMap.h>
#include <drogon/DrObject.h>
#include <drogon/HttpBinder.h>
#include <drogon/IntranetIpFilter.h>
#include <drogon/LocalHostFilter.h>
#include <drogon/HttpFilter.h>
#include <drogon/MultiPart.h>
#include <drogon/NotFound.h>
#include <drogon/drogon_callbacks.h>

View File

@ -20,6 +20,10 @@
#include <drogon/HttpResponse.h>
#include <memory>
#ifdef __cpp_impl_coroutine
#include <drogon/utils/coroutine.h>
#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<T>, 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<void(const HttpResponsePtr &)> &&);
}
#ifdef __cpp_impl_coroutine
template <typename T, bool AutoCreation = true>
class HttpCoroFilter : public DrObject<T>, 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<HttpResponsePtr> doFilter(const HttpRequestPtr &req) = 0;
};
#endif
} // namespace drogon

View File

@ -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)

View File

@ -24,6 +24,7 @@
#include <mutex>
#include <algorithm>
#include <atomic>
#include <chrono>
#ifndef _WIN32
#include <unistd.h>
@ -1011,6 +1012,7 @@ void doTest(const HttpClientPtr &client, std::shared_ptr<test::Case> 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::Case> 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<double, std::milli> duration = end - start;
CHECK(duration.count() >= 2000);
}
catch (const std::exception &e)
{
FAIL("Unexpected exception, what()" + std::string(e.what()));
}
}());
#endif
}

View File

@ -0,0 +1,12 @@
//
// Created by wanchen.he on 2022/8/16.
//
#include "CoroFilter.h"
Task<HttpResponsePtr> CoroFilter::doFilter(const HttpRequestPtr& req)
{
int secs = std::stoi(req->getParameter("secs"));
co_await sleepCoro(trantor::EventLoop::getEventLoopOfCurrentThread(), secs);
co_return {};
}

View File

@ -0,0 +1,17 @@
//
// Created by wanchen.he on 2022/8/16.
//
#pragma once
#include <drogon/HttpFilter.h>
using namespace drogon;
class CoroFilter : public drogon::HttpCoroFilter<CoroFilter>
{
public:
Task<HttpResponsePtr> doFilter(const HttpRequestPtr &req) override;
CoroFilter()
{
LOG_DEBUG << "CoroFilter constructor";
}
};

View File

@ -11,6 +11,7 @@ class CoroTest : public drogon::HttpController<CoroTest>
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