Add some managers to reduce the size of the HttpAppFrameworkImpl code (#194)

This commit is contained in:
An Tao 2019-07-12 16:47:21 +08:00 committed by GitHub
parent b85d8c5a08
commit 9af87bb1c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 681 additions and 423 deletions

View File

@ -32,7 +32,7 @@ void TimeFilter::doFilter(const HttpRequestPtr &req,
{
Json::Value json;
json["result"] = "error";
json["message"] = "Visit interval should be at least 10 seconds";
json["message"] = "Access interval should be at least 10 seconds";
auto res = HttpResponse::newHttpJsonResponse(json);
cb(res);
return;

View File

@ -109,7 +109,7 @@ void doTest(const HttpClientPtr &client,
auto &json = resp1->jsonObject();
if (json && json->get("message", std::string(""))
.asString() ==
"Visit interval should be "
"Access interval should be "
"at least 10 "
"seconds")
{

View File

@ -16,9 +16,6 @@
#include "AOPAdvice.h"
#include "ConfigLoader.h"
#include "HttpServer.h"
#if USE_ORM
#include "../../orm_lib/src/DbClientLockFree.h"
#endif
#include <algorithm>
#include <drogon/CacheMap.h>
#include <drogon/DrClassMap.h>
@ -27,20 +24,22 @@
#include <drogon/HttpTypes.h>
#include <drogon/Session.h>
#include <drogon/utils/Utilities.h>
#include <fcntl.h>
#include <trantor/utils/AsyncFileLogger.h>
#include <json/json.h>
#include <fstream>
#include <iostream>
#include <json/json.h>
#include <memory>
#include <unordered_map>
#include <utility>
#include <tuple>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <trantor/utils/AsyncFileLogger.h>
#include <tuple>
#include <unistd.h>
#include <unordered_map>
#include <utility>
#include <uuid.h>
using namespace drogon;
@ -52,26 +51,7 @@ drogon::InitBeforeMainFunction drogon::HttpAppFrameworkImpl::_initFirst([]() {
LOG_TRACE << "Initialize the main event loop in the main thread";
});
});
namespace drogon
{
class DrogonFileLocker : public trantor::NonCopyable
{
public:
DrogonFileLocker()
{
_fd = open("/tmp/drogon.lock", O_TRUNC | O_CREAT, 0755);
flock(_fd, LOCK_EX);
}
~DrogonFileLocker()
{
close(_fd);
}
private:
int _fd = 0;
};
} // namespace drogon
static void godaemon(void)
{
printf("Initializing daemon mode\n");
@ -218,23 +198,6 @@ void HttpAppFrameworkImpl::setSSLFiles(const std::string &certPath,
_sslCertPath = certPath;
_sslKeyPath = keyPath;
}
void HttpAppFrameworkImpl::addListener(const std::string &ip,
uint16_t port,
bool useSSL,
const std::string &certFile,
const std::string &keyFile)
{
assert(!_running);
#ifndef USE_OPENSSL
if (useSSL)
{
LOG_ERROR << "Can't use SSL without OpenSSL found in your system";
}
#endif
_listeners.push_back(std::make_tuple(ip, port, useSSL, certFile, keyFile));
}
void HttpAppFrameworkImpl::run()
{
@ -320,131 +283,20 @@ void HttpAppFrameworkImpl::run()
_sharedLibManagerPtr = std::unique_ptr<SharedLibManager>(
new SharedLibManager(getLoop(), _libFilePaths));
}
std::vector<std::shared_ptr<HttpServer>> servers;
std::vector<std::shared_ptr<EventLoopThread>> loopThreads;
std::vector<trantor::EventLoop *> ioLoops;
#ifdef __linux__
for (size_t i = 0; i < _threadNum; i++)
{
LOG_TRACE << "thread num=" << _threadNum;
auto loopThreadPtr = std::make_shared<EventLoopThread>("DrogonIoLoop");
loopThreads.push_back(loopThreadPtr);
ioLoops.push_back(loopThreadPtr->getLoop());
for (auto const &listener : _listeners)
{
auto ip = std::get<0>(listener);
bool isIpv6 = ip.find(":") == std::string::npos ? false : true;
std::shared_ptr<HttpServer> serverPtr;
if (i == 0)
{
DrogonFileLocker lock;
// Check whether the port is in use.
TcpServer server(getLoop(),
InetAddress(ip, std::get<1>(listener), isIpv6),
"drogonPortTest",
true,
false);
serverPtr = std::make_shared<HttpServer>(
loopThreadPtr->getLoop(),
InetAddress(ip, std::get<1>(listener), isIpv6),
"drogon");
}
else
{
serverPtr = std::make_shared<HttpServer>(
loopThreadPtr->getLoop(),
InetAddress(ip, std::get<1>(listener), isIpv6),
"drogon");
}
if (std::get<2>(listener))
{
#ifdef USE_OPENSSL
auto cert = std::get<3>(listener);
auto key = std::get<4>(listener);
if (cert == "")
cert = _sslCertPath;
if (key == "")
key = _sslKeyPath;
if (cert == "" || key == "")
{
std::cerr
<< "You can't use https without cert file or key file"
<< std::endl;
exit(1);
}
serverPtr->enableSSL(cert, key);
#endif
}
serverPtr->setHttpAsyncCallback(
std::bind(&HttpAppFrameworkImpl::onAsyncRequest, this, _1, _2));
serverPtr->setNewWebsocketCallback(std::bind(
&HttpAppFrameworkImpl::onNewWebsockRequest, this, _1, _2, _3));
serverPtr->setConnectionCallback(
std::bind(&HttpAppFrameworkImpl::onConnection, this, _1));
serverPtr->kickoffIdleConnections(_idleConnectionTimeout);
serverPtr->start();
servers.push_back(serverPtr);
}
}
#else
auto loopThreadPtr =
std::make_shared<EventLoopThread>("DrogonListeningLoop");
loopThreads.push_back(loopThreadPtr);
auto ioLoopThreadPoolPtr =
std::make_shared<EventLoopThreadPool>(_threadNum);
for (auto const &listener : _listeners)
{
LOG_TRACE << "thread num=" << _threadNum;
auto ip = std::get<0>(listener);
bool isIpv6 = ip.find(":") == std::string::npos ? false : true;
auto serverPtr = std::make_shared<HttpServer>(
loopThreadPtr->getLoop(),
InetAddress(ip, std::get<1>(listener), isIpv6),
"drogon");
if (std::get<2>(listener))
{
#ifdef USE_OPENSSL
auto cert = std::get<3>(listener);
auto key = std::get<4>(listener);
if (cert == "")
cert = _sslCertPath;
if (key == "")
key = _sslKeyPath;
if (cert == "" || key == "")
{
std::cerr << "You can't use https without cert file or key file"
<< std::endl;
exit(1);
}
serverPtr->enableSSL(cert, key);
#endif
}
serverPtr->setIoLoopThreadPool(ioLoopThreadPoolPtr);
serverPtr->setHttpAsyncCallback(
std::bind(&HttpAppFrameworkImpl::onAsyncRequest, this, _1, _2));
serverPtr->setNewWebsocketCallback(std::bind(
&HttpAppFrameworkImpl::onNewWebsockRequest, this, _1, _2, _3));
serverPtr->setConnectionCallback(
std::bind(&HttpAppFrameworkImpl::onConnection, this, _1));
serverPtr->kickoffIdleConnections(_idleConnectionTimeout);
serverPtr->start();
servers.push_back(serverPtr);
}
auto serverIoLoops = ioLoopThreadPoolPtr->getLoops();
for (auto serverIoLoop : serverIoLoops)
{
ioLoops.push_back(serverIoLoop);
}
#endif
// Create all listeners.
auto ioLoops = _listenerManager.createListeners(
std::bind(&HttpAppFrameworkImpl::onAsyncRequest, this, _1, _2),
std::bind(&HttpAppFrameworkImpl::onNewWebsockRequest, this, _1, _2, _3),
std::bind(&HttpAppFrameworkImpl::onConnection, this, _1),
_idleConnectionTimeout,
_sslCertPath,
_sslKeyPath,
_threadNum);
#if USE_ORM
// A fast database client instance should be created in the main event loop,
// so put the main loop into ioLoops.
ioLoops.push_back(getLoop());
createDbClients(ioLoops);
_dbClientManager.createDbClients(ioLoops);
ioLoops.pop_back();
#endif
_httpCtrlsRouter.init(ioLoops);
@ -454,33 +306,8 @@ void HttpAppFrameworkImpl::run()
if (_useSession)
{
if (_sessionTimeout > 0)
{
size_t wheelNum = 1;
size_t bucketNum = 0;
if (_sessionTimeout < 500)
{
bucketNum = _sessionTimeout + 1;
}
else
{
auto tmpTimeout = _sessionTimeout;
bucketNum = 100;
while (tmpTimeout > 100)
{
wheelNum++;
tmpTimeout = tmpTimeout / 100;
}
}
_sessionMapPtr = std::unique_ptr<CacheMap<std::string, SessionPtr>>(
new CacheMap<std::string, SessionPtr>(
getLoop(), 1.0, wheelNum, bucketNum));
}
else if (_sessionTimeout == 0)
{
_sessionMapPtr = std::unique_ptr<CacheMap<std::string, SessionPtr>>(
new CacheMap<std::string, SessionPtr>(getLoop(), 0, 0, 0));
}
_sessionManagerPtr = std::unique_ptr<SessionManager>(
new SessionManager(getLoop(), _sessionTimeout));
}
// Initialize plugins
@ -494,74 +321,11 @@ void HttpAppFrameworkImpl::run()
}
// Let listener event loops run when everything is ready.
for (auto &loopTh : loopThreads)
{
loopTh->run();
}
_listenerManager.startListening();
getLoop()->loop();
}
#if USE_ORM
void HttpAppFrameworkImpl::createDbClients(
const std::vector<trantor::EventLoop *> &ioloops)
{
assert(_dbClientsMap.empty());
assert(_dbFastClientsMap.empty());
for (auto &dbInfo : _dbInfos)
{
if (dbInfo._isFast)
{
for (auto *loop : ioloops)
{
if (dbInfo._dbType == drogon::orm::ClientType::Sqlite3)
{
LOG_ERROR << "Sqlite3 don't support fast mode";
abort();
}
if (dbInfo._dbType == drogon::orm::ClientType::PostgreSQL ||
dbInfo._dbType == drogon::orm::ClientType::Mysql)
{
_dbFastClientsMap[dbInfo._name][loop] =
std::shared_ptr<drogon::orm::DbClient>(
new drogon::orm::DbClientLockFree(
dbInfo._connectionInfo,
loop,
dbInfo._dbType,
dbInfo._connectionNumber));
}
}
}
else
{
if (dbInfo._dbType == drogon::orm::ClientType::PostgreSQL)
{
#if USE_POSTGRESQL
_dbClientsMap[dbInfo._name] =
drogon::orm::DbClient::newPgClient(
dbInfo._connectionInfo, dbInfo._connectionNumber);
#endif
}
else if (dbInfo._dbType == drogon::orm::ClientType::Mysql)
{
#if USE_MYSQL
_dbClientsMap[dbInfo._name] =
drogon::orm::DbClient::newMysqlClient(
dbInfo._connectionInfo, dbInfo._connectionNumber);
#endif
}
else if (dbInfo._dbType == drogon::orm::ClientType::Sqlite3)
{
#if USE_SQLITE3
_dbClientsMap[dbInfo._name] =
drogon::orm::DbClient::newSqlite3Client(
dbInfo._connectionInfo, dbInfo._connectionNumber);
#endif
}
}
}
}
#endif
void HttpAppFrameworkImpl::onConnection(const TcpConnectionPtr &conn)
void HttpAppFrameworkImpl::onConnection(const trantor::TcpConnectionPtr &conn)
{
static std::mutex mtx;
LOG_TRACE << "connect!!!" << _maxConnectionNum
@ -691,25 +455,12 @@ void HttpAppFrameworkImpl::onAsyncRequest(
bool needSetJsessionid = false;
if (_useSession)
{
if (sessionId == "")
if (sessionId.empty())
{
sessionId = utils::getUuid().c_str();
needSetJsessionid = true;
_sessionMapPtr->insert(sessionId,
std::make_shared<Session>(),
_sessionTimeout);
}
else
{
if (_sessionMapPtr->find(sessionId) == false)
{
_sessionMapPtr->insert(sessionId,
std::make_shared<Session>(),
_sessionTimeout);
}
}
(std::dynamic_pointer_cast<HttpRequestImpl>(req))
->setSession((*_sessionMapPtr)[sessionId]);
req->setSession(_sessionManagerPtr->getSession(sessionId));
}
// Route to controller
@ -773,93 +524,6 @@ HttpAppFramework::~HttpAppFramework()
{
}
#if USE_ORM
orm::DbClientPtr HttpAppFrameworkImpl::getDbClient(const std::string &name)
{
assert(_dbClientsMap.find(name) != _dbClientsMap.end());
return _dbClientsMap[name];
}
orm::DbClientPtr HttpAppFrameworkImpl::getFastDbClient(const std::string &name)
{
assert(_dbFastClientsMap[name].find(
trantor::EventLoop::getEventLoopOfCurrentThread()) !=
_dbFastClientsMap[name].end());
return _dbFastClientsMap[name]
[trantor::EventLoop::getEventLoopOfCurrentThread()];
}
void HttpAppFrameworkImpl::createDbClient(const std::string &dbType,
const std::string &host,
const u_short port,
const std::string &databaseName,
const std::string &userName,
const std::string &password,
const size_t connectionNum,
const std::string &filename,
const std::string &name,
const bool isFast)
{
assert(!_running);
auto connStr = utils::formattedString("host=%s port=%u dbname=%s user=%s",
host.c_str(),
port,
databaseName.c_str(),
userName.c_str());
if (!password.empty())
{
connStr += " password=";
connStr += password;
}
std::string type = dbType;
std::transform(type.begin(), type.end(), type.begin(), tolower);
DbInfo info;
info._connectionInfo = connStr;
info._connectionNumber = connectionNum;
info._isFast = isFast;
info._name = name;
if (type == "postgresql")
{
#if USE_POSTGRESQL
info._dbType = orm::ClientType::PostgreSQL;
_dbInfos.push_back(info);
#else
std::cout
<< "The PostgreSQL is not supported by drogon, please install "
"the development library first."
<< std::endl;
exit(1);
#endif
}
else if (type == "mysql")
{
#if USE_MYSQL
info._dbType = orm::ClientType::Mysql;
_dbInfos.push_back(info);
#else
std::cout << "The Mysql is not supported by drogon, please install the "
"development library first."
<< std::endl;
exit(1);
#endif
}
else if (type == "sqlite3")
{
#if USE_SQLITE3
std::string sqlite3ConnStr = "filename=" + filename;
info._connectionInfo = sqlite3ConnStr;
info._dbType = orm::ClientType::Sqlite3;
_dbInfos.push_back(info);
#else
std::cout
<< "The Sqlite3 is not supported by drogon, please install the "
"development library first."
<< std::endl;
exit(1);
#endif
}
}
#endif
void HttpAppFrameworkImpl::forward(
const HttpRequestImplPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback,

View File

@ -20,11 +20,15 @@
#include "HttpResponseImpl.h"
#include "HttpSimpleControllersRouter.h"
#include "PluginsManager.h"
#include "ListenerManager.h"
#include "SharedLibManager.h"
#include "WebSocketConnectionImpl.h"
#include "WebsocketControllersRouter.h"
#include "StaticFileRouter.h"
#include "SessionManager.h"
#if USE_ORM
#include "../../orm_lib/src/DbClientManager.h"
#endif
#include <drogon/HttpAppFramework.h>
#include <drogon/HttpSimpleController.h>
#include <drogon/version.h>
@ -81,7 +85,11 @@ class HttpAppFrameworkImpl : public HttpAppFramework
uint16_t port,
bool useSSL = false,
const std::string &certFile = "",
const std::string &keyFile = "") override;
const std::string &keyFile = "") override
{
assert(!_running);
_listenerManager.addListener(ip, port, useSSL, certFile, keyFile);
}
virtual void setThreadNum(size_t threadNum) override;
virtual size_t getThreadNum() const override
{
@ -308,7 +316,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
{
// Destroy the following objects before _loop destruction
_sharedLibManagerPtr.reset();
_sessionMapPtr.reset();
_sessionManagerPtr.reset();
}
virtual bool isRunning() override
@ -338,9 +346,15 @@ class HttpAppFrameworkImpl : public HttpAppFramework
#if USE_ORM
virtual orm::DbClientPtr getDbClient(
const std::string &name = "default") override;
const std::string &name = "default") override
{
return _dbClientManager.getDbClient(name);
}
virtual orm::DbClientPtr getFastDbClient(
const std::string &name = "default") override;
const std::string &name = "default") override
{
return _dbClientManager.getFastDbClient(name);
}
virtual void createDbClient(const std::string &dbType,
const std::string &host,
const u_short port,
@ -350,7 +364,20 @@ class HttpAppFrameworkImpl : public HttpAppFramework
const size_t connectionNum = 1,
const std::string &filename = "",
const std::string &name = "default",
const bool isFast = false) override;
const bool isFast = false) override
{
assert(!_running);
_dbClientManager.createDbClient(dbType,
host,
port,
databaseName,
userName,
password,
connectionNum,
filename,
name,
isFast);
}
#endif
inline static HttpAppFrameworkImpl &instance()
@ -377,7 +404,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
const HttpRequestImplPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback,
const WebSocketConnectionImplPtr &wsConnPtr);
void onConnection(const TcpConnectionPtr &conn);
void onConnection(const trantor::TcpConnectionPtr &conn);
void addHttpPath(const std::string &path,
const internal::HttpBinderBasePtr &binder,
const std::vector<HttpMethod> &validMethods,
@ -389,15 +416,10 @@ class HttpAppFrameworkImpl : public HttpAppFramework
size_t _sessionTimeout = 0;
size_t _idleConnectionTimeout = 60;
bool _useSession = false;
std::vector<
std::tuple<std::string, uint16_t, bool, std::string, std::string>>
_listeners;
ListenerManager _listenerManager;
std::string _serverHeader =
"Server: drogon/" + drogon::getVersion() + "\r\n";
typedef std::shared_ptr<Session> SessionPtr;
std::unique_ptr<CacheMap<std::string, SessionPtr>> _sessionMapPtr;
HttpControllersRouter _httpCtrlsRouter;
HttpSimpleControllersRouter _httpSimpleCtrlsRouter;
StaticFileRouter _staticFileRouter;
@ -434,25 +456,13 @@ class HttpAppFrameworkImpl : public HttpAppFramework
size_t _clientMaxMemoryBodySize = 64 * 1024;
size_t _clientMaxWebSocketMessageSize = 128 * 1024;
std::string _homePageFile = "index.html";
std::unique_ptr<SessionManager> _sessionManagerPtr;
// Json::Value _customConfig;
Json::Value _jsonConfig;
PluginsManager _pluginsManager;
HttpResponsePtr _custom404;
#if USE_ORM
std::map<std::string, orm::DbClientPtr> _dbClientsMap;
struct DbInfo
{
std::string _name;
std::string _connectionInfo;
orm::ClientType _dbType;
bool _isFast;
size_t _connectionNumber;
};
std::vector<DbInfo> _dbInfos;
std::map<std::string, std::map<trantor::EventLoop *, orm::DbClientPtr>>
_dbFastClientsMap;
void createDbClients(const std::vector<trantor::EventLoop *> &ioloops);
orm::DbClientManager _dbClientManager;
#endif
static InitBeforeMainFunction _initFirst;
std::vector<std::function<bool(const trantor::InetAddress &,

View File

@ -19,6 +19,7 @@
#include <algorithm>
#include <stdlib.h>
using namespace trantor;
using namespace drogon;
using namespace std::placeholders;

View File

@ -102,7 +102,7 @@ void HttpRequestImpl::parseParameters() const
// }
}
void HttpRequestImpl::appendToBuffer(MsgBuffer *output) const
void HttpRequestImpl::appendToBuffer(trantor::MsgBuffer *output) const
{
switch (_method)
{

View File

@ -34,9 +34,6 @@
#include <thread>
#include <unordered_map>
using std::string;
using namespace trantor;
namespace drogon
{
class HttpRequestImpl : public HttpRequest
@ -289,7 +286,7 @@ class HttpRequestImpl : public HttpRequest
_cookies[key] = value;
}
void appendToBuffer(MsgBuffer *output) const;
void appendToBuffer(trantor::MsgBuffer *output) const;
virtual SessionPtr session() const override
{

View File

@ -22,7 +22,6 @@
#include <trantor/net/TcpConnection.h>
#include <trantor/utils/MsgBuffer.h>
using namespace trantor;
namespace drogon
{
class HttpRequestParser
@ -40,7 +39,7 @@ class HttpRequestParser
explicit HttpRequestParser(const trantor::TcpConnectionPtr &connPtr);
// return false if any error
bool parseRequest(MsgBuffer *buf);
bool parseRequest(trantor::MsgBuffer *buf);
bool gotAll() const
{

View File

@ -26,7 +26,6 @@
#include <trantor/utils/MsgBuffer.h>
#include <unordered_map>
using namespace trantor;
namespace drogon
{
class HttpResponseImpl : public HttpResponse

View File

@ -21,7 +21,6 @@
#include <trantor/net/TcpConnection.h>
#include <trantor/utils/MsgBuffer.h>
using namespace trantor;
namespace drogon
{
class HttpResponseParser
@ -44,7 +43,7 @@ class HttpResponseParser
// default copy-ctor, dtor and assignment are fine
// return false if any error
bool parseResponse(MsgBuffer *buf);
bool parseResponse(trantor::MsgBuffer *buf);
bool gotAll() const
{

View File

@ -24,30 +24,28 @@
#include <trantor/net/callbacks.h>
#include <trantor/utils/NonCopyable.h>
using namespace trantor;
namespace drogon
{
class HttpRequest;
class HttpResponse;
typedef std::shared_ptr<HttpRequest> HttpRequestPtr;
typedef std::function<void(const HttpRequestImplPtr &,
std::function<void(const HttpResponsePtr &)> &&)>
HttpAsyncCallback;
typedef std::function<void(const HttpRequestImplPtr &,
std::function<void(const HttpResponsePtr &)> &&,
const WebSocketConnectionImplPtr &)>
WebSocketNewAsyncCallback;
class HttpServer : trantor::NonCopyable
{
public:
typedef std::function<void(const HttpRequestImplPtr &,
std::function<void(const HttpResponsePtr &)> &&)>
HttpAsyncCallback;
typedef std::function<void(const HttpRequestImplPtr &,
std::function<void(const HttpResponsePtr &)> &&,
const WebSocketConnectionImplPtr &)>
WebSocketNewAsyncCallback;
HttpServer(EventLoop *loop,
const InetAddress &listenAddr,
HttpServer(trantor::EventLoop *loop,
const trantor::InetAddress &listenAddr,
const std::string &name);
~HttpServer();
EventLoop *getLoop() const
trantor::EventLoop *getLoop() const
{
return _server.getLoop();
}
@ -60,7 +58,7 @@ class HttpServer : trantor::NonCopyable
{
_newWebsocketCallback = cb;
}
void setConnectionCallback(const ConnectionCallback &cb)
void setConnectionCallback(const trantor::ConnectionCallback &cb)
{
_connectionCallback = cb;
}
@ -95,10 +93,11 @@ class HttpServer : trantor::NonCopyable
#endif
private:
void onConnection(const TcpConnectionPtr &conn);
void onMessage(const TcpConnectionPtr &, MsgBuffer *);
void onRequest(const TcpConnectionPtr &, const HttpRequestImplPtr &);
void sendResponse(const TcpConnectionPtr &,
void onConnection(const trantor::TcpConnectionPtr &conn);
void onMessage(const trantor::TcpConnectionPtr &, trantor::MsgBuffer *);
void onRequest(const trantor::TcpConnectionPtr &,
const HttpRequestImplPtr &);
void sendResponse(const trantor::TcpConnectionPtr &,
const HttpResponsePtr &,
bool isHeadMethod);
trantor::TcpServer _server;

187
lib/src/ListenerManager.cc Normal file
View File

@ -0,0 +1,187 @@
/**
*
* ListenerManager.cc
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#include "ListenerManager.h"
#include <trantor/utils/config.h>
#include <trantor/utils/Logger.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
namespace drogon
{
class DrogonFileLocker : public trantor::NonCopyable
{
public:
DrogonFileLocker()
{
_fd = open("/tmp/drogon.lock", O_TRUNC | O_CREAT, 0755);
flock(_fd, LOCK_EX);
}
~DrogonFileLocker()
{
close(_fd);
}
private:
int _fd = 0;
};
} // namespace drogon
using namespace trantor;
using namespace drogon;
void ListenerManager::addListener(const std::string &ip,
uint16_t port,
bool useSSL,
const std::string &certFile,
const std::string &keyFile)
{
#ifndef USE_OPENSSL
if (useSSL)
{
LOG_ERROR << "Can't use SSL without OpenSSL found in your system";
}
#endif
_listeners.emplace_back(ip, port, useSSL, certFile, keyFile);
}
std::vector<trantor::EventLoop *> ListenerManager::createListeners(
const HttpAsyncCallback &httpCallback,
const WebSocketNewAsyncCallback &webSocketCallback,
const ConnectionCallback &connectionCallback,
size_t connectionTimeout,
const std::string &globalCertFile,
const std::string &globalKeyFile,
size_t threadNum)
{
#ifdef __linux__
std::vector<trantor::EventLoop *> ioLoops;
for (size_t i = 0; i < threadNum; i++)
{
LOG_TRACE << "thread num=" << threadNum;
auto loopThreadPtr = std::make_shared<EventLoopThread>("DrogonIoLoop");
_listeningloopThreads.push_back(loopThreadPtr);
ioLoops.push_back(loopThreadPtr->getLoop());
for (auto const &listener : _listeners)
{
auto const &ip = listener._ip;
bool isIpv6 = ip.find(":") == std::string::npos ? false : true;
std::shared_ptr<HttpServer> serverPtr;
if (i == 0)
{
DrogonFileLocker lock;
// Check whether the port is in use.
TcpServer server(app().getLoop(),
InetAddress(ip, listener._port, isIpv6),
"drogonPortTest",
true,
false);
serverPtr = std::make_shared<HttpServer>(
loopThreadPtr->getLoop(),
InetAddress(ip, listener._port, isIpv6),
"drogon");
}
else
{
serverPtr = std::make_shared<HttpServer>(
loopThreadPtr->getLoop(),
InetAddress(ip, listener._port, isIpv6),
"drogon");
}
if (listener._useSSL)
{
#ifdef USE_OPENSSL
auto cert = listener._certFile;
auto key = listener._keyFile;
if (cert == "")
cert = globalCertFile;
if (key == "")
key = globalKeyFile;
if (cert == "" || key == "")
{
std::cerr
<< "You can't use https without cert file or key file"
<< std::endl;
exit(1);
}
serverPtr->enableSSL(cert, key);
#endif
}
serverPtr->setHttpAsyncCallback(httpCallback);
serverPtr->setNewWebsocketCallback(webSocketCallback);
serverPtr->setConnectionCallback(connectionCallback);
serverPtr->kickoffIdleConnections(connectionTimeout);
serverPtr->start();
_servers.push_back(serverPtr);
}
}
#else
auto loopThreadPtr =
std::make_shared<EventLoopThread>("DrogonListeningLoop");
_listeningloopThreads.push_back(loopThreadPtr);
_ioLoopThreadPoolPtr = std::make_shared<EventLoopThreadPool>(threadNum);
for (auto const &listener : _listeners)
{
LOG_TRACE << "thread num=" << threadNum;
auto ip = listener._ip;
bool isIpv6 = ip.find(":") == std::string::npos ? false : true;
auto serverPtr = std::make_shared<HttpServer>(
loopThreadPtr->getLoop(),
InetAddress(ip, listener._port, isIpv6),
"drogon");
if (listener._useSSL)
{
#ifdef USE_OPENSSL
auto cert = listener._certFile;
auto key = listener._keyFile;
if (cert == "")
cert = globalCertFile;
if (key == "")
key = globalKeyFile;
if (cert == "" || key == "")
{
std::cerr << "You can't use https without cert file or key file"
<< std::endl;
exit(1);
}
serverPtr->enableSSL(cert, key);
#endif
}
serverPtr->setIoLoopThreadPool(_ioLoopThreadPoolPtr);
serverPtr->setHttpAsyncCallback(httpCallback);
serverPtr->setNewWebsocketCallback(webSocketCallback);
serverPtr->setConnectionCallback(connectionCallback);
serverPtr->kickoffIdleConnections(connectionTimeout);
serverPtr->start();
_servers.push_back(serverPtr);
}
auto ioLoops = _ioLoopThreadPoolPtr->getLoops();
#endif
return ioLoops;
}
void ListenerManager::startListening()
{
for (auto &loopThread : _listeningloopThreads)
{
loopThread->run();
}
}

73
lib/src/ListenerManager.h Normal file
View File

@ -0,0 +1,73 @@
/**
*
* ListenerManager.h
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#pragma once
#include "HttpServer.h"
#include <trantor/utils/NonCopyable.h>
#include <trantor/net/EventLoopThread.h>
#include <string>
#include <vector>
#include <memory>
namespace drogon
{
class ListenerManager : public trantor::NonCopyable
{
public:
void addListener(const std::string &ip,
uint16_t port,
bool useSSL = false,
const std::string &certFile = "",
const std::string &keyFile = "");
std::vector<trantor::EventLoop *> createListeners(
const HttpAsyncCallback &httpCallback,
const WebSocketNewAsyncCallback &webSocketCallback,
const trantor::ConnectionCallback &connectionCallback,
size_t connectionTimeout,
const std::string &globalCertFile,
const std::string &globalKeyFile,
size_t threadNum);
void startListening();
private:
struct ListenerInfo
{
ListenerInfo(const std::string &ip,
uint16_t port,
bool useSSL,
const std::string &certFile,
const std::string &keyFile)
: _ip(ip),
_port(port),
_useSSL(useSSL),
_certFile(certFile),
_keyFile(keyFile)
{
}
std::string _ip;
uint16_t _port;
bool _useSSL;
std::string _certFile;
std::string _keyFile;
};
std::vector<ListenerInfo> _listeners;
std::vector<std::shared_ptr<HttpServer>> _servers;
std::vector<std::shared_ptr<trantor::EventLoopThread>> _listeningloopThreads;
std::string _sslCertPath;
std::string _sslKeyPath;
std::shared_ptr<trantor::EventLoopThreadPool> _ioLoopThreadPoolPtr;
};
} // namespace drogon

64
lib/src/SessionManager.cc Normal file
View File

@ -0,0 +1,64 @@
/**
*
* SessionManager.cc
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#include "SessionManager.h"
using namespace drogon;
SessionManager::SessionManager(trantor::EventLoop *loop, size_t timeout)
: _loop(loop), _timeout(timeout)
{
assert(_timeout >= 0);
if (_timeout > 0)
{
size_t wheelNum = 1;
size_t bucketNum = 0;
if (_timeout < 500)
{
bucketNum = _timeout + 1;
}
else
{
auto tmpTimeout = _timeout;
bucketNum = 100;
while (tmpTimeout > 100)
{
wheelNum++;
tmpTimeout = tmpTimeout / 100;
}
}
_sessionMapPtr = std::unique_ptr<CacheMap<std::string, SessionPtr>>(
new CacheMap<std::string, SessionPtr>(
_loop, 1.0, wheelNum, bucketNum));
}
else if (_timeout == 0)
{
_sessionMapPtr = std::unique_ptr<CacheMap<std::string, SessionPtr>>(
new CacheMap<std::string, SessionPtr>(_loop, 0, 0, 0));
}
}
SessionPtr SessionManager::getSession(const std::string &sessionID)
{
assert(!sessionID.empty());
SessionPtr sessionPtr;
std::lock_guard<std::mutex> lock(_mapMutex);
if (_sessionMapPtr->findAndFetch(sessionID, sessionPtr) == false)
{
sessionPtr = std::make_shared<Session>();
_sessionMapPtr->insert(sessionID, sessionPtr, _timeout);
return sessionPtr;
}
return sessionPtr;
}

44
lib/src/SessionManager.h Normal file
View File

@ -0,0 +1,44 @@
/**
*
* SessionManager.h
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#pragma once
#include <trantor/utils/NonCopyable.h>
#include <trantor/net/EventLoop.h>
#include <drogon/Session.h>
#include <drogon/CacheMap.h>
#include <memory>
#include <string>
#include <mutex>
namespace drogon
{
class SessionManager : public trantor::NonCopyable
{
public:
SessionManager(trantor::EventLoop *loop, size_t timeout);
~SessionManager()
{
_sessionMapPtr.reset();
}
SessionPtr getSession(const std::string &sessionID);
private:
std::unique_ptr<CacheMap<std::string, SessionPtr>> _sessionMapPtr;
std::mutex _mapMutex;
trantor::EventLoop *_loop;
size_t _timeout;
};
} // namespace drogon

View File

@ -42,7 +42,7 @@ class DbClient : public trantor::NonCopyable
{
public:
virtual ~DbClient(){};
/// Create new database client with multiple connections;
/// Create a new database client with multiple connections;
/**
* @param connInfo: Connection string with some parameters,
* each parameter setting is in the form keyword = value. Spaces around the
@ -163,12 +163,13 @@ class DbClient : public trantor::NonCopyable
/**
* @param commitCallback: the callback with which user can get the
* submitting result, The Boolean type parameter in the callback function
* indicates whether the transaction was submitted successfully. NOTE: The
* callback only indicates the result of the 'commit' command, which is the
* last step of the transaction. If the transaction has been automatically
* or manually rolled back, the callback will never be executed. You can
* also use the setCommitCallback() method of a transaction object to set
* the callback.
* indicates whether the transaction was submitted successfully.
*
* NOTE: The callback only indicates the result of the 'commit' command,
* which is the last step of the transaction. If the transaction has been
* automatically or manually rolled back, the callback will never be
* executed. You can also use the setCommitCallback() method of a
* transaction object to set the callback.
*/
virtual std::shared_ptr<Transaction> newTransaction(
const std::function<void(bool)> &commitCallback = nullptr) = 0;

View File

@ -0,0 +1,151 @@
/**
*
* DbClientManager.cc
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#include "DbClientManager.h"
#include "DbClientLockFree.h"
#include <drogon/utils/Utilities.h>
#include <algorithm>
using namespace drogon::orm;
using namespace drogon;
void DbClientManager::createDbClients(
const std::vector<trantor::EventLoop *> &ioloops)
{
assert(_dbClientsMap.empty());
assert(_dbFastClientsMap.empty());
for (auto &dbInfo : _dbInfos)
{
if (dbInfo._isFast)
{
for (auto *loop : ioloops)
{
if (dbInfo._dbType == drogon::orm::ClientType::Sqlite3)
{
LOG_ERROR << "Sqlite3 don't support fast mode";
abort();
}
if (dbInfo._dbType == drogon::orm::ClientType::PostgreSQL ||
dbInfo._dbType == drogon::orm::ClientType::Mysql)
{
_dbFastClientsMap[dbInfo._name][loop] =
std::shared_ptr<drogon::orm::DbClient>(
new drogon::orm::DbClientLockFree(
dbInfo._connectionInfo,
loop,
dbInfo._dbType,
dbInfo._connectionNumber));
}
}
}
else
{
if (dbInfo._dbType == drogon::orm::ClientType::PostgreSQL)
{
#if USE_POSTGRESQL
_dbClientsMap[dbInfo._name] =
drogon::orm::DbClient::newPgClient(
dbInfo._connectionInfo, dbInfo._connectionNumber);
#endif
}
else if (dbInfo._dbType == drogon::orm::ClientType::Mysql)
{
#if USE_MYSQL
_dbClientsMap[dbInfo._name] =
drogon::orm::DbClient::newMysqlClient(
dbInfo._connectionInfo, dbInfo._connectionNumber);
#endif
}
else if (dbInfo._dbType == drogon::orm::ClientType::Sqlite3)
{
#if USE_SQLITE3
_dbClientsMap[dbInfo._name] =
drogon::orm::DbClient::newSqlite3Client(
dbInfo._connectionInfo, dbInfo._connectionNumber);
#endif
}
}
}
}
void DbClientManager::createDbClient(const std::string &dbType,
const std::string &host,
const u_short port,
const std::string &databaseName,
const std::string &userName,
const std::string &password,
const size_t connectionNum,
const std::string &filename,
const std::string &name,
const bool isFast)
{
auto connStr = utils::formattedString("host=%s port=%u dbname=%s user=%s",
host.c_str(),
port,
databaseName.c_str(),
userName.c_str());
if (!password.empty())
{
connStr += " password=";
connStr += password;
}
std::string type = dbType;
std::transform(type.begin(), type.end(), type.begin(), tolower);
DbInfo info;
info._connectionInfo = connStr;
info._connectionNumber = connectionNum;
info._isFast = isFast;
info._name = name;
if (type == "postgresql")
{
#if USE_POSTGRESQL
info._dbType = orm::ClientType::PostgreSQL;
_dbInfos.push_back(info);
#else
std::cout
<< "The PostgreSQL is not supported by drogon, please install "
"the development library first."
<< std::endl;
exit(1);
#endif
}
else if (type == "mysql")
{
#if USE_MYSQL
info._dbType = orm::ClientType::Mysql;
_dbInfos.push_back(info);
#else
std::cout << "The Mysql is not supported by drogon, please install the "
"development library first."
<< std::endl;
exit(1);
#endif
}
else if (type == "sqlite3")
{
#if USE_SQLITE3
std::string sqlite3ConnStr = "filename=" + filename;
info._connectionInfo = sqlite3ConnStr;
info._dbType = orm::ClientType::Sqlite3;
_dbInfos.push_back(info);
#else
std::cout
<< "The Sqlite3 is not supported by drogon, please install the "
"development library first."
<< std::endl;
exit(1);
#endif
}
}

View File

@ -0,0 +1,70 @@
/**
*
* DbClientManager.h
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#pragma once
#include <drogon/orm/DbClient.h>
#include <trantor/utils/NonCopyable.h>
#include <trantor/net/EventLoop.h>
#include <string>
namespace drogon
{
namespace orm
{
class DbClientManager : public trantor::NonCopyable
{
public:
void createDbClients(const std::vector<trantor::EventLoop *> &ioloops);
DbClientPtr getDbClient(const std::string &name)
{
assert(_dbClientsMap.find(name) != _dbClientsMap.end());
return _dbClientsMap[name];
}
DbClientPtr getFastDbClient(const std::string &name)
{
assert(_dbFastClientsMap[name].find(
trantor::EventLoop::getEventLoopOfCurrentThread()) !=
_dbFastClientsMap[name].end());
return _dbFastClientsMap
[name][trantor::EventLoop::getEventLoopOfCurrentThread()];
}
void createDbClient(const std::string &dbType,
const std::string &host,
const u_short port,
const std::string &databaseName,
const std::string &userName,
const std::string &password,
const size_t connectionNum,
const std::string &filename,
const std::string &name,
const bool isFast);
private:
std::map<std::string, DbClientPtr> _dbClientsMap;
struct DbInfo
{
std::string _name;
std::string _connectionInfo;
ClientType _dbType;
bool _isFast;
size_t _connectionNumber;
};
std::vector<DbInfo> _dbInfos;
std::map<std::string, std::map<trantor::EventLoop *, orm::DbClientPtr>>
_dbFastClientsMap;
};
} // namespace orm
} // namespace drogon