Modifying the routing algorithm of HTTP controllers

This commit is contained in:
an-tao 2019-01-17 00:12:58 +08:00
parent b05e56ff6b
commit 5c96c4e6a2
4 changed files with 95 additions and 49 deletions

View File

@ -1,6 +1,18 @@
#include "api_v1_ApiTest.h" #include "api_v1_ApiTest.h"
using namespace api::v1; using namespace api::v1;
//add definition of your processing function here //add definition of your processing function here
void ApiTest::rootGet(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback)
{
auto res = HttpResponse::newHttpResponse();
res->setBody("ROOT Get!!!");
callback(res);
}
void ApiTest::rootPost(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback)
{
auto res = HttpResponse::newHttpResponse();
res->setBody("ROOT Post!!!");
callback(res);
}
void ApiTest::get(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback, int p1, std::string &&p2) void ApiTest::get(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback, int p1, std::string &&p2)
{ {
HttpViewData data; HttpViewData data;

View File

@ -10,7 +10,9 @@ class ApiTest : public drogon::HttpController<ApiTest>
public: public:
METHOD_LIST_BEGIN METHOD_LIST_BEGIN
//use METHOD_ADD to add your custom processing function here; //use METHOD_ADD to add your custom processing function here;
METHOD_ADD(ApiTest::get, "/get/{2}/{1}", Get); //path will be /api/v1/apitest/get/{arg2}/{arg1} METHOD_ADD(ApiTest::rootGet, "", Get);
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 METHOD_ADD(ApiTest::your_method_name, "/{1}/List?P2={2}", Get); //path will be /api/v1/apitest/{arg1}/list
METHOD_ADD(ApiTest::staticApi, "/static", Get, Post); METHOD_ADD(ApiTest::staticApi, "/static", Get, Post);
METHOD_ADD(ApiTest::get2, "/get/{1}", Get); METHOD_ADD(ApiTest::get2, "/get/{1}", Get);
@ -20,6 +22,8 @@ class ApiTest : public drogon::HttpController<ApiTest>
void your_method_name(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback, double p1, int p2) const; void your_method_name(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback, double p1, int p2) const;
void staticApi(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback); void staticApi(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback);
void get2(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback, std::string &&p1); void get2(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback, std::string &&p1);
void rootGet(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback);
void rootPost(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback);
}; };
} // namespace v1 } // namespace v1
} // namespace api } // namespace api

View File

@ -112,11 +112,11 @@ void HttpAppFrameworkImpl::setFileTypes(const std::vector<std::string> &types)
void HttpAppFrameworkImpl::initRegex() void HttpAppFrameworkImpl::initRegex()
{ {
std::string regString; std::string regString;
for (auto &binder : _ctrlVector) for (auto &router : _ctrlVector)
{ {
std::regex reg("\\(\\[\\^/\\]\\*\\)"); std::regex reg("\\(\\[\\^/\\]\\*\\)");
std::string tmp = std::regex_replace(binder.pathParameterPattern, reg, "[^/]*"); std::string tmp = std::regex_replace(router.pathParameterPattern, reg, "[^/]*");
binder._regex = std::regex(binder.pathParameterPattern, std::regex_constants::icase); router._regex = std::regex(router.pathParameterPattern, std::regex_constants::icase);
regString.append("(").append(tmp).append(")|"); regString.append("(").append(tmp).append(")|");
} }
if (regString.length() > 0) if (regString.length() > 0)
@ -239,23 +239,52 @@ void HttpAppFrameworkImpl::addHttpPath(const std::string &path,
paras = results.suffix(); paras = results.suffix();
} }
} }
struct CtrlBinder _binder; auto pathParameterPattern = std::regex_replace(originPath, regex, "([^/]*)");
_binder.parameterPlaces = std::move(places); auto binderInfo = CtrlBinderPtr(new CtrlBinder);
_binder.queryParametersPlaces = std::move(parametersPlaces); binderInfo->filtersName = filters;
_binder.binderPtr = binder; binderInfo->binderPtr = binder;
_binder.filtersName = filters; binderInfo->parameterPlaces = std::move(places);
_binder.pathParameterPattern = std::regex_replace(originPath, regex, "([^/]*)"); binderInfo->queryParametersPlaces = std::move(parametersPlaces);
{
std::lock_guard<std::mutex> guard(_ctrlMutex);
for (auto &router : _ctrlVector)
{
if (router.pathParameterPattern == pathParameterPattern)
{
if (validMethods.size() > 0)
{
for (auto method : validMethods)
{
router._binders[method] = binderInfo;
}
}
else
{
for (int i = 0; i < Invalid; i++)
router._binders[i] = binderInfo;
}
return;
}
}
}
struct HttpControllerRouterItem router;
router.pathParameterPattern = pathParameterPattern;
if (validMethods.size() > 0) if (validMethods.size() > 0)
{ {
_binder._validMethodsFlags.resize(Invalid, 0);
for (auto method : validMethods) for (auto method : validMethods)
{ {
_binder._validMethodsFlags[method] = 1; router._binders[method] = binderInfo;
} }
} }
else
{
for (int i = 0; i < Invalid; i++)
router._binders[i] = binderInfo;
}
{ {
std::lock_guard<std::mutex> guard(_ctrlMutex); std::lock_guard<std::mutex> guard(_ctrlMutex);
_ctrlVector.push_back(std::move(_binder)); _ctrlVector.push_back(std::move(router));
} }
} }
void HttpAppFrameworkImpl::registerHttpController(const std::string &pathPattern, void HttpAppFrameworkImpl::registerHttpController(const std::string &pathPattern,
@ -1062,28 +1091,25 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, const s
if (result[i].str() == req->path() && i <= _ctrlVector.size()) if (result[i].str() == req->path() && i <= _ctrlVector.size())
{ {
size_t ctlIndex = i - 1; size_t ctlIndex = i - 1;
auto &binder = _ctrlVector[ctlIndex]; auto &router = _ctrlVector[ctlIndex];
//LOG_TRACE << "got http access,regex=" << binder.pathParameterPattern; //LOG_TRACE << "got http access,regex=" << binder.pathParameterPattern;
if (binder._validMethodsFlags.size() > 0) assert(Invalid > req->method());
auto &binder = router._binders[req->method()];
if (!binder)
{ {
assert(binder._validMethodsFlags.size() > req->method()); //Invalid Http Method
if (binder._validMethodsFlags[req->method()] == 0) auto res = drogon::HttpResponse::newHttpResponse();
{ res->setStatusCode(HttpResponse::k405MethodNotAllowed);
//Invalid Http Method callback(res);
auto res = drogon::HttpResponse::newHttpResponse(); return;
res->setStatusCode(HttpResponse::k405MethodNotAllowed);
callback(res);
return;
}
} }
auto &filters = binder.filtersName;
doFilters(filters, req, callback, needSetJsessionid, session_id, [=]() {
auto &binder = _ctrlVector[ctlIndex];
auto &filters = binder->filtersName;
doFilters(filters, req, callback, needSetJsessionid, session_id, [=]() {
HttpResponsePtr responsePtr; HttpResponsePtr responsePtr;
{ {
std::lock_guard<std::mutex> guard(*(binder.binderMtx)); std::lock_guard<std::mutex> guard(*(binder->binderMtx));
responsePtr = binder.responsePtr.lock(); responsePtr = binder->responsePtr.lock();
} }
if (responsePtr) if (responsePtr)
{ {
@ -1103,28 +1129,28 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, const s
return; return;
} }
std::vector<std::string> params(binder.parameterPlaces.size()); std::vector<std::string> params(binder->parameterPlaces.size());
std::smatch r; std::smatch r;
if (std::regex_match(req->path(), r, binder._regex)) if (std::regex_match(req->path(), r, router._regex))
{ {
for (size_t j = 1; j < r.size(); j++) for (size_t j = 1; j < r.size(); j++)
{ {
size_t place = binder.parameterPlaces[j - 1]; size_t place = binder->parameterPlaces[j - 1];
if (place > params.size()) if (place > params.size())
params.resize(place); params.resize(place);
params[place - 1] = r[j].str(); params[place - 1] = r[j].str();
LOG_TRACE << "place=" << place << " para:" << params[place - 1]; LOG_TRACE << "place=" << place << " para:" << params[place - 1];
} }
} }
if (binder.queryParametersPlaces.size() > 0) if (binder->queryParametersPlaces.size() > 0)
{ {
auto qureyPara = req->getParameters(); auto qureyPara = req->getParameters();
for (auto parameter : qureyPara) for (auto parameter : qureyPara)
{ {
if (binder.queryParametersPlaces.find(parameter.first) != if (binder->queryParametersPlaces.find(parameter.first) !=
binder.queryParametersPlaces.end()) binder->queryParametersPlaces.end())
{ {
auto place = binder.queryParametersPlaces.find(parameter.first)->second; auto place = binder->queryParametersPlaces.find(parameter.first)->second;
if (place > params.size()) if (place > params.size())
params.resize(place); params.resize(place);
params[place - 1] = parameter.second; params[place - 1] = parameter.second;
@ -1138,7 +1164,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, const s
paraList.push_back(std::move(p)); paraList.push_back(std::move(p));
} }
binder.binderPtr->handleHttpRequest(paraList, req, [=](const HttpResponsePtr &resp) { binder->binderPtr->handleHttpRequest(paraList, req, [=](const HttpResponsePtr &resp) {
LOG_TRACE << "http resp:needSetJsessionid=" << needSetJsessionid << ";JSESSIONID=" << session_id; LOG_TRACE << "http resp:needSetJsessionid=" << needSetJsessionid << ";JSESSIONID=" << session_id;
auto newResp = resp; auto newResp = resp;
if (resp->expiredTime() >= 0) if (resp->expiredTime() >= 0)
@ -1147,9 +1173,9 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, const s
std::dynamic_pointer_cast<HttpResponseImpl>(resp)->makeHeaderString(); std::dynamic_pointer_cast<HttpResponseImpl>(resp)->makeHeaderString();
{ {
auto &binderIterm = _ctrlVector[ctlIndex]; auto &binderIterm = _ctrlVector[ctlIndex];
std::lock_guard<std::mutex> guard(*(binderIterm.binderMtx)); std::lock_guard<std::mutex> guard(*(binder->binderMtx));
_responseCacheMap->insert(binderIterm.pathParameterPattern, resp, resp->expiredTime()); _responseCacheMap->insert(binderIterm.pathParameterPattern, resp, resp->expiredTime());
binderIterm.responsePtr = resp; binder->responsePtr = resp;
} }
} }
if (needSetJsessionid) if (needSetJsessionid)

View File

@ -152,7 +152,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
const std::string &session_id, const std::string &session_id,
const std::function<void()> &missCallback); const std::function<void()> &missCallback);
// //
struct ControllerAndFiltersName struct SimpleControllerRouterItem
{ {
std::string controllerName; std::string controllerName;
std::vector<std::string> filtersName; std::vector<std::string> filtersName;
@ -161,29 +161,33 @@ class HttpAppFrameworkImpl : public HttpAppFramework
std::weak_ptr<HttpResponse> responsePtr; std::weak_ptr<HttpResponse> responsePtr;
std::mutex _mutex; std::mutex _mutex;
}; };
std::unordered_map<std::string, ControllerAndFiltersName> _simpCtrlMap; std::unordered_map<std::string, SimpleControllerRouterItem> _simpCtrlMap;
std::mutex _simpCtrlMutex; std::mutex _simpCtrlMutex;
struct WSCtrlAndFiltersName struct WebSocketControllerRouterItem
{ {
WebSocketControllerBasePtr controller; WebSocketControllerBasePtr controller;
std::vector<std::string> filtersName; std::vector<std::string> filtersName;
}; };
std::unordered_map<std::string, WSCtrlAndFiltersName> _websockCtrlMap; std::unordered_map<std::string, WebSocketControllerRouterItem> _websockCtrlMap;
std::mutex _websockCtrlMutex; std::mutex _websockCtrlMutex;
struct CtrlBinder struct CtrlBinder
{ {
std::string pathParameterPattern;
std::vector<size_t> parameterPlaces;
std::map<std::string, size_t> queryParametersPlaces;
internal::HttpBinderBasePtr binderPtr; internal::HttpBinderBasePtr binderPtr;
std::vector<std::string> filtersName; std::vector<std::string> filtersName;
std::vector<size_t> parameterPlaces;
std::map<std::string, size_t> queryParametersPlaces;
std::unique_ptr<std::mutex> binderMtx = std::unique_ptr<std::mutex>(new std::mutex); std::unique_ptr<std::mutex> binderMtx = std::unique_ptr<std::mutex>(new std::mutex);
std::weak_ptr<HttpResponse> responsePtr; std::weak_ptr<HttpResponse> responsePtr;
std::vector<int> _validMethodsFlags;
std::regex _regex;
}; };
std::vector<CtrlBinder> _ctrlVector; typedef std::shared_ptr<CtrlBinder> CtrlBinderPtr;
struct HttpControllerRouterItem
{
std::string pathParameterPattern;
std::regex _regex;
CtrlBinderPtr _binders[Invalid]; //The enum value Invalid is the http methods number
};
std::vector<HttpControllerRouterItem> _ctrlVector;
std::mutex _ctrlMutex; std::mutex _ctrlMutex;
std::regex _ctrlRegex; std::regex _ctrlRegex;