diff --git a/examples/simple_example/TimeFilter.cpp b/examples/simple_example/TimeFilter.cpp index e68f3911..3b8f110f 100755 --- a/examples/simple_example/TimeFilter.cpp +++ b/examples/simple_example/TimeFilter.cpp @@ -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; diff --git a/examples/simple_example_test/main.cc b/examples/simple_example_test/main.cc index ec704f31..dc7524d1 100644 --- a/examples/simple_example_test/main.cc +++ b/examples/simple_example_test/main.cc @@ -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") { diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 8f0aa8f8..85eda76e 100644 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -16,9 +16,6 @@ #include "AOPAdvice.h" #include "ConfigLoader.h" #include "HttpServer.h" -#if USE_ORM -#include "../../orm_lib/src/DbClientLockFree.h" -#endif #include #include #include @@ -27,20 +24,22 @@ #include #include #include -#include +#include +#include + #include #include -#include #include +#include +#include +#include + +#include #include #include #include #include -#include -#include #include -#include -#include #include 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( new SharedLibManager(getLoop(), _libFilePaths)); } - std::vector> servers; - std::vector> loopThreads; - - std::vector ioLoops; - -#ifdef __linux__ - for (size_t i = 0; i < _threadNum; i++) - { - LOG_TRACE << "thread num=" << _threadNum; - auto loopThreadPtr = std::make_shared("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 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( - loopThreadPtr->getLoop(), - InetAddress(ip, std::get<1>(listener), isIpv6), - "drogon"); - } - else - { - serverPtr = std::make_shared( - 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("DrogonListeningLoop"); - loopThreads.push_back(loopThreadPtr); - auto ioLoopThreadPoolPtr = - std::make_shared(_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( - 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>( - new CacheMap( - getLoop(), 1.0, wheelNum, bucketNum)); - } - else if (_sessionTimeout == 0) - { - _sessionMapPtr = std::unique_ptr>( - new CacheMap(getLoop(), 0, 0, 0)); - } + _sessionManagerPtr = std::unique_ptr( + 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 &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( - 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(), - _sessionTimeout); } - else - { - if (_sessionMapPtr->find(sessionId) == false) - { - _sessionMapPtr->insert(sessionId, - std::make_shared(), - _sessionTimeout); - } - } - (std::dynamic_pointer_cast(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 &&callback, diff --git a/lib/src/HttpAppFrameworkImpl.h b/lib/src/HttpAppFrameworkImpl.h index 4be39c3e..738bffe6 100644 --- a/lib/src/HttpAppFrameworkImpl.h +++ b/lib/src/HttpAppFrameworkImpl.h @@ -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 #include #include @@ -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 &&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 &validMethods, @@ -389,15 +416,10 @@ class HttpAppFrameworkImpl : public HttpAppFramework size_t _sessionTimeout = 0; size_t _idleConnectionTimeout = 60; bool _useSession = false; - std::vector< - std::tuple> - _listeners; + ListenerManager _listenerManager; std::string _serverHeader = "Server: drogon/" + drogon::getVersion() + "\r\n"; - typedef std::shared_ptr SessionPtr; - std::unique_ptr> _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 _sessionManagerPtr; // Json::Value _customConfig; Json::Value _jsonConfig; PluginsManager _pluginsManager; HttpResponsePtr _custom404; #if USE_ORM - std::map _dbClientsMap; - struct DbInfo - { - std::string _name; - std::string _connectionInfo; - orm::ClientType _dbType; - bool _isFast; - size_t _connectionNumber; - }; - std::vector _dbInfos; - std::map> - _dbFastClientsMap; - void createDbClients(const std::vector &ioloops); + orm::DbClientManager _dbClientManager; #endif static InitBeforeMainFunction _initFirst; std::vector #include +using namespace trantor; using namespace drogon; using namespace std::placeholders; diff --git a/lib/src/HttpRequestImpl.cc b/lib/src/HttpRequestImpl.cc index 618630e6..f71a9499 100644 --- a/lib/src/HttpRequestImpl.cc +++ b/lib/src/HttpRequestImpl.cc @@ -102,7 +102,7 @@ void HttpRequestImpl::parseParameters() const // } } -void HttpRequestImpl::appendToBuffer(MsgBuffer *output) const +void HttpRequestImpl::appendToBuffer(trantor::MsgBuffer *output) const { switch (_method) { diff --git a/lib/src/HttpRequestImpl.h b/lib/src/HttpRequestImpl.h index 34da57d4..024cd600 100644 --- a/lib/src/HttpRequestImpl.h +++ b/lib/src/HttpRequestImpl.h @@ -34,9 +34,6 @@ #include #include -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 { diff --git a/lib/src/HttpRequestParser.h b/lib/src/HttpRequestParser.h index c1cb60e4..4d6bc287 100644 --- a/lib/src/HttpRequestParser.h +++ b/lib/src/HttpRequestParser.h @@ -22,7 +22,6 @@ #include #include -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 { diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index 79f733b9..abb8bf63 100644 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -26,7 +26,6 @@ #include #include -using namespace trantor; namespace drogon { class HttpResponseImpl : public HttpResponse diff --git a/lib/src/HttpResponseParser.h b/lib/src/HttpResponseParser.h index 4c9b3b22..a34bfa21 100644 --- a/lib/src/HttpResponseParser.h +++ b/lib/src/HttpResponseParser.h @@ -21,7 +21,6 @@ #include #include -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 { diff --git a/lib/src/HttpServer.h b/lib/src/HttpServer.h index e681eeca..bed4fdb5 100644 --- a/lib/src/HttpServer.h +++ b/lib/src/HttpServer.h @@ -24,30 +24,28 @@ #include #include -using namespace trantor; namespace drogon { class HttpRequest; class HttpResponse; typedef std::shared_ptr HttpRequestPtr; +typedef std::function &&)> + HttpAsyncCallback; +typedef std::function &&, + const WebSocketConnectionImplPtr &)> + WebSocketNewAsyncCallback; class HttpServer : trantor::NonCopyable { public: - typedef std::function &&)> - HttpAsyncCallback; - typedef std::function &&, - 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; diff --git a/lib/src/ListenerManager.cc b/lib/src/ListenerManager.cc new file mode 100644 index 00000000..a22deeba --- /dev/null +++ b/lib/src/ListenerManager.cc @@ -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 +#include + +#include +#include +#include +#include +#include +#include + +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 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 ioLoops; + for (size_t i = 0; i < threadNum; i++) + { + LOG_TRACE << "thread num=" << threadNum; + auto loopThreadPtr = std::make_shared("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 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( + loopThreadPtr->getLoop(), + InetAddress(ip, listener._port, isIpv6), + "drogon"); + } + else + { + serverPtr = std::make_shared( + 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("DrogonListeningLoop"); + _listeningloopThreads.push_back(loopThreadPtr); + _ioLoopThreadPoolPtr = std::make_shared(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( + 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(); + } +} \ No newline at end of file diff --git a/lib/src/ListenerManager.h b/lib/src/ListenerManager.h new file mode 100644 index 00000000..b1c5cca6 --- /dev/null +++ b/lib/src/ListenerManager.h @@ -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 +#include +#include +#include +#include + +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 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 _listeners; + std::vector> _servers; + std::vector> _listeningloopThreads; + std::string _sslCertPath; + std::string _sslKeyPath; + std::shared_ptr _ioLoopThreadPoolPtr; +}; + +} // namespace drogon \ No newline at end of file diff --git a/lib/src/SessionManager.cc b/lib/src/SessionManager.cc new file mode 100644 index 00000000..eb567e9c --- /dev/null +++ b/lib/src/SessionManager.cc @@ -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>( + new CacheMap( + _loop, 1.0, wheelNum, bucketNum)); + } + else if (_timeout == 0) + { + _sessionMapPtr = std::unique_ptr>( + new CacheMap(_loop, 0, 0, 0)); + } +} + +SessionPtr SessionManager::getSession(const std::string &sessionID) +{ + assert(!sessionID.empty()); + SessionPtr sessionPtr; + std::lock_guard lock(_mapMutex); + if (_sessionMapPtr->findAndFetch(sessionID, sessionPtr) == false) + { + sessionPtr = std::make_shared(); + _sessionMapPtr->insert(sessionID, sessionPtr, _timeout); + return sessionPtr; + } + return sessionPtr; +} \ No newline at end of file diff --git a/lib/src/SessionManager.h b/lib/src/SessionManager.h new file mode 100644 index 00000000..9d8fa9e0 --- /dev/null +++ b/lib/src/SessionManager.h @@ -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 +#include +#include +#include + +#include +#include +#include + +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> _sessionMapPtr; + std::mutex _mapMutex; + trantor::EventLoop *_loop; + size_t _timeout; +}; +} // namespace drogon \ No newline at end of file diff --git a/orm_lib/inc/drogon/orm/DbClient.h b/orm_lib/inc/drogon/orm/DbClient.h index b33699a6..97575dfa 100644 --- a/orm_lib/inc/drogon/orm/DbClient.h +++ b/orm_lib/inc/drogon/orm/DbClient.h @@ -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 newTransaction( const std::function &commitCallback = nullptr) = 0; diff --git a/orm_lib/src/DbClientManager.cc b/orm_lib/src/DbClientManager.cc new file mode 100644 index 00000000..f828399a --- /dev/null +++ b/orm_lib/src/DbClientManager.cc @@ -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 +#include + +using namespace drogon::orm; +using namespace drogon; + +void DbClientManager::createDbClients( + const std::vector &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( + 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 + } +} \ No newline at end of file diff --git a/orm_lib/src/DbClientManager.h b/orm_lib/src/DbClientManager.h new file mode 100644 index 00000000..a5b6bfb7 --- /dev/null +++ b/orm_lib/src/DbClientManager.h @@ -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 +#include +#include +#include + +namespace drogon +{ +namespace orm +{ +class DbClientManager : public trantor::NonCopyable +{ + public: + void createDbClients(const std::vector &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 _dbClientsMap; + struct DbInfo + { + std::string _name; + std::string _connectionInfo; + ClientType _dbType; + bool _isFast; + size_t _connectionNumber; + }; + std::vector _dbInfos; + std::map> + _dbFastClientsMap; +}; +} // namespace orm +} // namespace drogon