Merge pull request #42 from an-tao/dev

Optimize filters and controllers
This commit is contained in:
An Tao 2019-01-25 17:06:43 +08:00 committed by GitHub
commit 26e3cff668
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 144 additions and 74 deletions

View File

@ -15,5 +15,9 @@ class TestController : public drogon::HttpSimpleController<TestController>
PATH_ADD("/tpost", Post);
PATH_ADD("/slow", "TimeFilter", Get);
PATH_LIST_END
TestController()
{
LOG_DEBUG << "TestController constructor";
}
};
} // namespace example

View File

@ -13,4 +13,8 @@ class TimeFilter : public drogon::HttpFilter<TimeFilter>
virtual void doFilter(const HttpRequestPtr &req,
const FilterCallback &cb,
const FilterChainCallback &ccb) override;
TimeFilter()
{
LOG_DEBUG << "TimeFilter constructor";
}
};

View File

@ -10,7 +10,7 @@ class ApiTest : public drogon::HttpController<ApiTest>
public:
METHOD_LIST_BEGIN
//use METHOD_ADD to add your custom processing function here;
METHOD_ADD(ApiTest::rootGet, "", "TimeFilter", Get, "drogon::LocalHostFilter");
METHOD_ADD(ApiTest::rootGet, "", "TimeFilter", Get, "drogon::LocalHostFilter", "drogon::InnerIpFilter");
METHOD_ADD(ApiTest::rootPost, "", Post);
METHOD_ADD(ApiTest::get, "/get/{2}/{1}", Get); //path will be /api/v1/apitest/get/{arg2}/{arg1}
METHOD_ADD(ApiTest::your_method_name, "/{1}/List?P2={2}", Get); //path will be /api/v1/apitest/{arg1}/list
@ -28,6 +28,12 @@ class ApiTest : public drogon::HttpController<ApiTest>
void rootPost(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback);
void jsonTest(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback);
void formTest(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback);
public:
ApiTest()
{
LOG_DEBUG << "ApiTest constructor!";
}
};
} // namespace v1
} // namespace api

View File

@ -32,6 +32,7 @@ class DrClassMap
public:
static void registerClass(const std::string &className, const DrAllocFunc &func);
static DrObjectBase *newObject(const std::string &className);
static const std::shared_ptr<DrObjectBase> &getSingleInstance(const std::string &className);
static std::vector<std::string> getAllClassName();
protected:

View File

@ -74,6 +74,7 @@ class HttpAppFramework : public trantor::NonCopyable
* Usually the thread calling this method is main thread of the application;
*/
virtual void run() = 0;
virtual bool isRunning() = 0;
///Quit the event loop
/**

View File

@ -13,6 +13,7 @@
*/
#include <drogon/DrClassMap.h>
#include <drogon/DrObject.h>
#include <iostream>
using namespace drogon;
//std::map <std::string,DrAllocFunc> * DrClassMap::classMap=nullptr;
@ -33,6 +34,17 @@ DrObjectBase *DrClassMap::newObject(const std::string &className)
else
return nullptr;
}
const std::shared_ptr<DrObjectBase> &DrClassMap::getSingleInstance(const std::string &className)
{
static std::unordered_map<std::string, std::shared_ptr<DrObjectBase>> singleInstanceMap;
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
auto iter = singleInstanceMap.find(className);
if (iter != singleInstanceMap.end())
return iter->second;
singleInstanceMap[className] = std::shared_ptr<DrObjectBase>(newObject(className));
return singleInstanceMap[className];
}
std::vector<std::string> DrClassMap::getAllClassName()
{
std::vector<std::string> ret;

View File

@ -20,25 +20,26 @@
using namespace drogon;
static void doFilterChain(const std::shared_ptr<std::queue<std::shared_ptr<HttpFilterBase>>> &chain,
const HttpRequestImplPtr &req,
const std::shared_ptr<const std::function<void(const HttpResponsePtr &)>> &callbackPtr,
bool needSetJsessionid,
const std::shared_ptr<std::string> &sessionIdPtr,
std::function<void()> &&missCallback)
static void doFilterChains(const std::vector<std::shared_ptr<HttpFilterBase>> &filters,
size_t index,
const HttpRequestImplPtr &req,
const std::shared_ptr<const std::function<void(const HttpResponsePtr &)>> &callbackPtr,
bool needSetJsessionid,
const std::shared_ptr<std::string> &sessionIdPtr,
std::function<void()> &&missCallback)
{
if (chain && chain->size() > 0)
if (index < filters.size())
{
auto filter = chain->front();
chain->pop();
auto &filter = filters[index];
filter->doFilter(req,
[=](HttpResponsePtr res) {
if (needSetJsessionid)
res->addCookie("JSESSIONID", *sessionIdPtr);
(*callbackPtr)(res);
},
[=, missCallback = std::move(missCallback)]() mutable {
doFilterChain(chain, req, callbackPtr, needSetJsessionid, sessionIdPtr, std::move(missCallback));
[=, &filters, missCallback = std::move(missCallback)]() mutable {
doFilterChains(filters, index + 1, req, callbackPtr, needSetJsessionid, sessionIdPtr, std::move(missCallback));
});
}
else
@ -46,28 +47,31 @@ static void doFilterChain(const std::shared_ptr<std::queue<std::shared_ptr<HttpF
missCallback();
}
}
void FiltersFunction::doFilters(const std::vector<std::string> &filters,
std::vector<std::shared_ptr<HttpFilterBase>> FiltersFunction::createFilters(const std::vector<std::string> &filterNames)
{
std::vector<std::shared_ptr<HttpFilterBase>> filters;
for (auto const &filter : filterNames)
{
auto _object = DrClassMap::getSingleInstance(filter);
auto _filter = std::dynamic_pointer_cast<HttpFilterBase>(_object);
if (_filter)
filters.push_back(_filter);
else
{
LOG_ERROR << "filter " << filter << " not found";
}
}
return filters;
}
void FiltersFunction::doFilters(const std::vector<std::shared_ptr<HttpFilterBase>> &filters,
const HttpRequestImplPtr &req,
const std::shared_ptr<const std::function<void(const HttpResponsePtr &)>> &callbackPtr,
bool needSetJsessionid,
const std::shared_ptr<std::string> &sessionIdPtr,
std::function<void()> &&missCallback)
{
std::shared_ptr<std::queue<std::shared_ptr<HttpFilterBase>>> filterPtrs;
if (!filters.empty())
{
filterPtrs = std::make_shared<std::queue<std::shared_ptr<HttpFilterBase>>>();
for (auto const &filter : filters)
{
auto _object = std::shared_ptr<DrObjectBase>(DrClassMap::newObject(filter));
auto _filter = std::dynamic_pointer_cast<HttpFilterBase>(_object);
if (_filter)
filterPtrs->push(_filter);
else
{
LOG_ERROR << "filter " << filter << " not found";
}
}
}
doFilterChain(filterPtrs, req, callbackPtr, needSetJsessionid, sessionIdPtr, std::move(missCallback));
}
doFilterChains(filters, 0, req, callbackPtr, needSetJsessionid, sessionIdPtr, std::move(missCallback));
}

View File

@ -15,14 +15,18 @@
#pragma once
#include "HttpRequestImpl.h"
#include <drogon/HttpFilter.h>
#include <string>
#include <vector>
#include <memory>
namespace drogon
{
struct FiltersFunction
{
static void doFilters(const std::vector<std::string> &filters,
static std::vector<std::shared_ptr<HttpFilterBase>> createFilters(const std::vector<std::string> &filterNames);
static void doFilters(const std::vector<std::shared_ptr<HttpFilterBase>> &filters,
const HttpRequestImplPtr &req,
const std::shared_ptr<const std::function<void(const HttpResponsePtr &)>> &callbackPtr,
bool needSetJsessionid,

View File

@ -278,6 +278,8 @@ void HttpAppFrameworkImpl::run()
std::vector<std::shared_ptr<HttpServer>> servers;
std::vector<std::shared_ptr<EventLoopThread>> loopThreads;
_httpCtrlsRouter.init();
_httpSimpleCtrlsRouter.init();
_websockCtrlsRouter.init();
for (auto const &listener : _listeners)
{
LOG_TRACE << "thread num=" << _threadNum;

View File

@ -95,7 +95,10 @@ class HttpAppFrameworkImpl : public HttpAppFramework
_sharedLibManagerPtr.reset();
_sessionMapPtr.reset();
}
virtual bool isRunning() override
{
return _running;
}
virtual trantor::EventLoop *loop() override;
virtual void quit() override
{

View File

@ -29,6 +29,13 @@ void HttpControllersRouter::init()
std::string tmp = std::regex_replace(router._pathParameterPattern, reg, "[^/]*");
router._regex = std::regex(router._pathParameterPattern, std::regex_constants::icase);
regString.append("(").append(tmp).append(")|");
for(auto &binder:router._binders)
{
if(binder)
{
binder->_filters = FiltersFunction::createFilters(binder->_filterNames);
}
}
}
if (regString.length() > 0)
regString.resize(regString.length() - 1); //remove the last '|'
@ -91,7 +98,7 @@ void HttpControllersRouter::addHttpPath(const std::string &path,
}
auto pathParameterPattern = std::regex_replace(originPath, regex, "([^/]*)");
auto binderInfo = CtrlBinderPtr(new CtrlBinder);
binderInfo->_filtersName = filters;
binderInfo->_filterNames = filters;
binderInfo->_binderPtr = binder;
binderInfo->_parameterPlaces = std::move(places);
binderInfo->_queryParametersPlaces = std::move(parametersPlaces);
@ -168,9 +175,9 @@ void HttpControllersRouter::route(const HttpRequestImplPtr &req,
callback(res);
return;
}
auto &filters = binder->_filtersName;
if (!filters.empty())
if (!binder->_filters.empty())
{
auto &filters = binder->_filters;
auto sessionIdPtr = std::make_shared<std::string>(std::move(sessionId));
auto callbackPtr = std::make_shared<std::function<void(const HttpResponsePtr &)>>(std::move(callback));
FiltersFunction::doFilters(filters, req, callbackPtr, needSetJsessionid, sessionIdPtr, [=, &binder, &routerItem]() {

View File

@ -17,6 +17,7 @@
#include "HttpResponseImpl.h"
#include <trantor/utils/NonCopyable.h>
#include <drogon/HttpBinder.h>
#include <drogon/HttpFilter.h>
#include <vector>
#include <regex>
#include <string>
@ -29,7 +30,7 @@ class HttpAppFrameworkImpl;
class HttpControllersRouter : public trantor::NonCopyable
{
public:
HttpControllersRouter(){}
HttpControllersRouter() {}
void init();
void addHttpPath(const std::string &path,
const internal::HttpBinderBasePtr &binder,
@ -44,7 +45,8 @@ class HttpControllersRouter : public trantor::NonCopyable
struct CtrlBinder
{
internal::HttpBinderBasePtr _binderPtr;
std::vector<std::string> _filtersName;
std::vector<std::string> _filterNames;
std::vector<std::shared_ptr<HttpFilterBase>> _filters;
std::vector<size_t> _parameterPlaces;
std::map<std::string, size_t> _queryParametersPlaces;
std::mutex _binderMtx;

View File

@ -53,7 +53,7 @@ void HttpSimpleControllersRouter::registerHttpSimpleController(const std::string
}
auto &item = _simpCtrlMap[path];
item._controllerName = ctrlName;
item._filtersName = filters;
item._filterNames = filters;
item._validMethodsFlags.clear(); //There may be old data, first clear
if (validMethods.size() > 0)
{
@ -66,7 +66,7 @@ void HttpSimpleControllersRouter::registerHttpSimpleController(const std::string
auto controller = item._controller;
if (!controller)
{
auto _object = std::shared_ptr<DrObjectBase>(DrClassMap::newObject(ctrlName));
auto &_object = DrClassMap::getSingleInstance(ctrlName);
controller = std::dynamic_pointer_cast<HttpSimpleControllerBase>(_object);
item._controller = controller;
}
@ -95,7 +95,7 @@ void HttpSimpleControllersRouter::route(const HttpRequestImplPtr &req,
return;
}
}
auto &filters = ctrlInfo._filtersName;
auto &filters = ctrlInfo._filters;
if (!filters.empty())
{
auto sessionIdPtr = std::make_shared<std::string>(std::move(sessionId));
@ -119,7 +119,7 @@ void HttpSimpleControllersRouter::doControllerHandler(SimpleControllerRouterItem
bool needSetJsessionid,
std::string &&sessionId)
{
const std::string &ctrlName = item._controllerName;
const std::string &ctrlName = item._controllerName;
auto &controller = item._controller;
if (controller)
{
@ -183,4 +183,13 @@ void HttpSimpleControllersRouter::doControllerHandler(SimpleControllerRouterItem
callback(res);
}
}
void HttpSimpleControllersRouter::init()
{
for (auto &iter : _simpCtrlMap)
{
auto &item = iter.second;
item._filters = FiltersFunction::createFilters(item._filterNames);
}
}

View File

@ -18,6 +18,7 @@
#include <drogon/HttpSimpleController.h>
#include <trantor/utils/NonCopyable.h>
#include <drogon/HttpBinder.h>
#include <drogon/HttpFilter.h>
#include <vector>
#include <regex>
#include <string>
@ -40,13 +41,15 @@ class HttpSimpleControllersRouter : public trantor::NonCopyable
std::function<void(const HttpResponsePtr &)> &&callback,
bool needSetJsessionid,
std::string &&sessionId);
void init();
private:
HttpControllersRouter &_httpCtrlsRouter;
struct SimpleControllerRouterItem
{
std::string _controllerName;
std::vector<std::string> _filtersName;
std::vector<std::string> _filterNames;
std::vector<std::shared_ptr<HttpFilterBase>> _filters;
std::vector<int> _validMethodsFlags;
std::shared_ptr<HttpSimpleControllerBase> _controller;
std::shared_ptr<HttpResponse> _responsePtr;

View File

@ -35,8 +35,8 @@ void WebsocketControllersRouter::registerWebSocketController(const std::string &
assert(ctrlPtr);
std::lock_guard<std::mutex> guard(_websockCtrlMutex);
_websockCtrlMap[path].controller = ctrlPtr;
_websockCtrlMap[path].filtersName = filters;
_websockCtrlMap[path]._controller = ctrlPtr;
_websockCtrlMap[path]._filterNames = filters;
}
void WebsocketControllersRouter::route(const HttpRequestImplPtr &req,
@ -47,33 +47,30 @@ void WebsocketControllersRouter::route(const HttpRequestImplPtr &req,
if (!wsKey.empty())
{
// magic="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
WebSocketControllerBasePtr ctrlPtr;
std::vector<std::string> filtersName;
std::string pathLower(req->path());
std::transform(pathLower.begin(), pathLower.end(), pathLower.begin(), tolower);
auto iter = _websockCtrlMap.find(pathLower);
if (iter != _websockCtrlMap.end())
{
std::string pathLower(req->path());
std::transform(pathLower.begin(), pathLower.end(), pathLower.begin(), tolower);
std::lock_guard<std::mutex> guard(_websockCtrlMutex);
if (_websockCtrlMap.find(pathLower) != _websockCtrlMap.end())
auto &ctrlPtr = iter->second._controller;
auto &filters = iter->second._filters;
if (ctrlPtr)
{
ctrlPtr = _websockCtrlMap[pathLower].controller;
filtersName = _websockCtrlMap[pathLower].filtersName;
if (!filters.empty())
{
auto callbackPtr = std::make_shared<std::function<void(const HttpResponsePtr &)>>(std::move(callback));
FiltersFunction::doFilters(filters, req, callbackPtr, false, nullptr, [=]() mutable {
doControllerHandler(ctrlPtr, wsKey, req, std::move(*callbackPtr), wsConnPtr);
});
}
else
{
doControllerHandler(ctrlPtr, wsKey, req, std::move(callback), wsConnPtr);
}
return;
}
}
if (ctrlPtr)
{
if (!filtersName.empty())
{
auto callbackPtr = std::make_shared<std::function<void(const HttpResponsePtr &)>>(std::move(callback));
FiltersFunction::doFilters(filtersName, req, callbackPtr, false, nullptr, [=]() mutable {
doControllerHandler(ctrlPtr, wsKey, req, *callbackPtr, wsConnPtr);
});
}
else
{
doControllerHandler(ctrlPtr, wsKey, req, callback, wsConnPtr);
}
return;
}
}
auto resp = drogon::HttpResponse::newNotFoundResponse();
resp->setCloseConnection(true);
@ -83,7 +80,7 @@ void WebsocketControllersRouter::route(const HttpRequestImplPtr &req,
void WebsocketControllersRouter::doControllerHandler(const WebSocketControllerBasePtr &ctrlPtr,
std::string &wsKey,
const HttpRequestImplPtr &req,
const std::function<void(const HttpResponsePtr &)> &callback,
std::function<void(const HttpResponsePtr &)> &&callback,
const WebSocketConnectionPtr &wsConnPtr)
{
wsKey.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
@ -101,4 +98,12 @@ void WebsocketControllersRouter::doControllerHandler(const WebSocketControllerBa
wsConnImplPtr->setController(ctrlPtr);
ctrlPtr->handleNewConnection(req, wsConnPtr);
return;
}
void WebsocketControllersRouter::init()
{
for (auto &iter : _websockCtrlMap)
{
iter.second._filters = FiltersFunction::createFilters(iter.second._filterNames);
}
}

View File

@ -17,6 +17,7 @@
#include "HttpResponseImpl.h"
#include <trantor/utils/NonCopyable.h>
#include <drogon/WebSocketController.h>
#include <drogon/HttpFilter.h>
#include <vector>
#include <regex>
#include <string>
@ -29,19 +30,21 @@ class HttpAppFrameworkImpl;
class WebsocketControllersRouter : public trantor::NonCopyable
{
public:
WebsocketControllersRouter(){}
WebsocketControllersRouter() {}
void registerWebSocketController(const std::string &pathName,
const std::string &ctrlName,
const std::vector<std::string> &filters);
void route(const HttpRequestImplPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback,
const WebSocketConnectionPtr &wsConnPtr);
void init();
private:
struct WebSocketControllerRouterItem
{
WebSocketControllerBasePtr controller;
std::vector<std::string> filtersName;
WebSocketControllerBasePtr _controller;
std::vector<std::string> _filterNames;
std::vector<std::shared_ptr<HttpFilterBase>> _filters;
};
std::unordered_map<std::string, WebSocketControllerRouterItem> _websockCtrlMap;
std::mutex _websockCtrlMutex;
@ -49,7 +52,7 @@ class WebsocketControllersRouter : public trantor::NonCopyable
void doControllerHandler(const WebSocketControllerBasePtr &ctrlPtr,
std::string &wsKey,
const HttpRequestImplPtr &req,
const std::function<void(const HttpResponsePtr &)> &callback,
std::function<void(const HttpResponsePtr &)> &&callback,
const WebSocketConnectionPtr &wsConnPtr);
};
} // namespace drogon

@ -1 +1 @@
Subproject commit 4357d2ada95b020f0573888dd9341d40286c4224
Subproject commit 2c78af9ea6a7dac972940f61ba12c84a9915cab0