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"
using namespace api::v1;
//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)
{
HttpViewData data;

View File

@ -10,7 +10,9 @@ class ApiTest : public drogon::HttpController<ApiTest>
public:
METHOD_LIST_BEGIN
//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::staticApi, "/static", Get, Post);
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 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 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 api

View File

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

View File

@ -152,7 +152,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
const std::string &session_id,
const std::function<void()> &missCallback);
//
struct ControllerAndFiltersName
struct SimpleControllerRouterItem
{
std::string controllerName;
std::vector<std::string> filtersName;
@ -161,29 +161,33 @@ class HttpAppFrameworkImpl : public HttpAppFramework
std::weak_ptr<HttpResponse> responsePtr;
std::mutex _mutex;
};
std::unordered_map<std::string, ControllerAndFiltersName> _simpCtrlMap;
std::unordered_map<std::string, SimpleControllerRouterItem> _simpCtrlMap;
std::mutex _simpCtrlMutex;
struct WSCtrlAndFiltersName
struct WebSocketControllerRouterItem
{
WebSocketControllerBasePtr controller;
std::vector<std::string> filtersName;
};
std::unordered_map<std::string, WSCtrlAndFiltersName> _websockCtrlMap;
std::unordered_map<std::string, WebSocketControllerRouterItem> _websockCtrlMap;
std::mutex _websockCtrlMutex;
struct CtrlBinder
{
std::string pathParameterPattern;
std::vector<size_t> parameterPlaces;
std::map<std::string, size_t> queryParametersPlaces;
internal::HttpBinderBasePtr binderPtr;
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::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::regex _ctrlRegex;