From 0a3d27a5b08f2cbb923fc7aee4b69125323aec57 Mon Sep 17 00:00:00 2001 From: antao Date: Sat, 13 Oct 2018 21:49:37 +0800 Subject: [PATCH] Optimize response caching --- .gitignore | 1 + CMakeLists.txt | 2 - examples/simple_example/main.cc | 185 ++++--- lib/src/HttpAppFrameworkImpl.cc | 844 ++++++++++++++++---------------- lib/src/HttpResponseImpl.cc | 432 ++++++++-------- lib/src/HttpResponseImpl.h | 563 ++++++++++----------- lib/src/HttpServer.cc | 168 ++++--- 7 files changed, 1121 insertions(+), 1074 deletions(-) diff --git a/.gitignore b/.gitignore index 15506963..2d1f9b30 100755 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ lib/inc/drogon/config.h Doxyfile html/ latex/ +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index 76a375ea..625d6837 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,5 +136,3 @@ install(FILES ${drogon_util_headers} DESTINATION include/drogon/utils) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/lib/inc/drogon/version.h" DESTINATION include/drogon) - -#target_link_libraries(drogon trantor pthread) diff --git a/examples/simple_example/main.cc b/examples/simple_example/main.cc index 32a180b7..cc3ff5c8 100755 --- a/examples/simple_example/main.cc +++ b/examples/simple_example/main.cc @@ -6,141 +6,140 @@ #include using namespace drogon; -class A:public DrObjectBase +class A : public DrObjectBase { -public: - void handle(const HttpRequestPtr& req, - const std::function&callback, - int p1,const std::string &p2,const std::string &p3,int p4) const + public: + void handle(const HttpRequestPtr &req, + const std::function &callback, + int p1, const std::string &p2, const std::string &p3, int p4) const { HttpViewData data; - data.insert("title",std::string("ApiTest::get")); - std::map para; - para["int p1"]=std::to_string(p1); - para["string p2"]=p2; - para["string p3"]=p3; - para["int p4"]=std::to_string(p4); + data.insert("title", std::string("ApiTest::get")); + std::map para; + para["int p1"] = std::to_string(p1); + para["string p2"] = p2; + para["string p3"] = p3; + para["int p4"] = std::to_string(p4); - data.insert("parameters",para); - auto res=HttpResponse::newHttpViewResponse("ListParaView",data); + data.insert("parameters", para); + auto res = HttpResponse::newHttpViewResponse("ListParaView", data); callback(res); } - static void staticHandle(const HttpRequestPtr& req, - const std::function&callback, - int p1,const std::string &p2,const std::string &p3,int p4) + static void staticHandle(const HttpRequestPtr &req, + const std::function &callback, + int p1, const std::string &p2, const std::string &p3, int p4) { HttpViewData data; - data.insert("title",std::string("ApiTest::get")); - std::map para; - para["int p1"]=std::to_string(p1); - para["string p2"]=p2; - para["string p3"]=p3; - para["int p4"]=std::to_string(p4); + data.insert("title", std::string("ApiTest::get")); + std::map para; + para["int p1"] = std::to_string(p1); + para["string p2"] = p2; + para["string p3"] = p3; + para["int p4"] = std::to_string(p4); - data.insert("parameters",para); - auto res=HttpResponse::newHttpViewResponse("ListParaView",data); + data.insert("parameters", para); + auto res = HttpResponse::newHttpViewResponse("ListParaView", data); callback(res); } }; -class B:public DrObjectBase +class B : public DrObjectBase { -public: - void operator ()(const HttpRequestPtr& req,const std::function&callback,int p1,int p2) + public: + void operator()(const HttpRequestPtr &req, const std::function &callback, int p1, int p2) { HttpViewData data; - data.insert("title",std::string("ApiTest::get")); - std::map para; - para["p1"]=std::to_string(p1); - para["p2"]=std::to_string(p2); - data.insert("parameters",para); - auto res=HttpResponse::newHttpViewResponse("ListParaView",data); + data.insert("title", std::string("ApiTest::get")); + std::map para; + para["p1"] = std::to_string(p1); + para["p2"] = std::to_string(p2); + data.insert("parameters", para); + auto res = HttpResponse::newHttpViewResponse("ListParaView", data); callback(res); } }; namespace api { - namespace v1 +namespace v1 +{ +class Test : public HttpApiController +{ + public: + METHOD_LIST_BEGIN + METHOD_ADD(Test::get, "get/{2}/{1}", "drogon::GetFilter"); //path will be /api/v1/test/get/{arg2}/{arg1} + METHOD_ADD(Test::list, "/{2}/info", "drogon::GetFilter"); //path will be /api/v1/test/{arg2}/info + METHOD_LIST_END + void get(const HttpRequestPtr &req, const std::function &callback, int p1, int p2) const { - class Test:public HttpApiController - { - public: - METHOD_LIST_BEGIN - METHOD_ADD(Test::get,"get/{2}/{1}","drogon::GetFilter");//path will be /api/v1/test/get/{arg2}/{arg1} - METHOD_ADD(Test::list,"/{2}/info","drogon::GetFilter");//path will be /api/v1/test/{arg2}/info - METHOD_LIST_END - void get(const HttpRequestPtr& req,const std::function&callback,int p1,int p2) const - { - HttpViewData data; - data.insert("title",std::string("ApiTest::get")); - std::map para; - para["p1"]=std::to_string(p1); - para["p2"]=std::to_string(p2); - data.insert("parameters",para); - auto res=HttpResponse::newHttpViewResponse("ListParaView",data); - callback(res); - } - void list(const HttpRequestPtr& req,const std::function&callback,int p1,int p2) const - { - HttpViewData data; - data.insert("title",std::string("ApiTest::get")); - std::map para; - para["p1"]=std::to_string(p1); - para["p2"]=std::to_string(p2); - data.insert("parameters",para); - auto res=HttpResponse::newHttpViewResponse("ListParaView",data); - callback(res); - } - }; + HttpViewData data; + data.insert("title", std::string("ApiTest::get")); + std::map para; + para["p1"] = std::to_string(p1); + para["p2"] = std::to_string(p2); + data.insert("parameters", para); + auto res = HttpResponse::newHttpViewResponse("ListParaView", data); + callback(res); } -} + void list(const HttpRequestPtr &req, const std::function &callback, int p1, int p2) const + { + HttpViewData data; + data.insert("title", std::string("ApiTest::get")); + std::map para; + para["p1"] = std::to_string(p1); + para["p2"] = std::to_string(p2); + data.insert("parameters", para); + auto res = HttpResponse::newHttpViewResponse("ListParaView", data); + callback(res); + } +}; +} // namespace v1 +} // namespace api using namespace std::placeholders; int main() { - std::cout<&callback,int a,float b){ - LOG_DEBUG<<"int a="< &callback, int a, float b) { + LOG_DEBUG << "int a=" << a; + LOG_DEBUG << "float b=" << b; HttpViewData data; - data.insert("title",std::string("ApiTest::get")); - std::map para; - para["a"]=std::to_string(a); - para["b"]=std::to_string(b); - data.insert("parameters",para); - auto res=HttpResponse::newHttpViewResponse("ListParaView",data); + data.insert("title", std::string("ApiTest::get")); + std::map para; + para["a"] = std::to_string(a); + para["b"] = std::to_string(b); + data.insert("parameters", para); + auto res = HttpResponse::newHttpViewResponse("ListParaView", data); callback(res); }); B b; //functor example - drogon::HttpAppFramework::registerHttpApiMethod("/api/v1/handle3/{1}/{2}",b); + drogon::HttpAppFramework::registerHttpApiMethod("/api/v1/handle3/{1}/{2}", b); A tmp; - std::function&,int,const std::string &,const std::string &,int)> - func=std::bind(&A::handle,&tmp,_1,_2,_3,_4,_5,_6); + std::function &, int, const std::string &, const std::string &, int)> + func = std::bind(&A::handle, &tmp, _1, _2, _3, _4, _5, _6); //api example for std::function - drogon::HttpAppFramework::registerHttpApiMethod("/api/v1/handle4/{4}/{3}/{1}",func); + drogon::HttpAppFramework::registerHttpApiMethod("/api/v1/handle4/{4}/{3}/{1}", func); drogon::HttpAppFramework::instance().setDocumentRoot("./"); - drogon::HttpAppFramework::instance().enableSession(1200); + drogon::HttpAppFramework::instance().enableSession(60); //start app framework //drogon::HttpAppFramework::instance().enableDynamicViewsLoading({"/tmp/views"}); - drogon::HttpAppFramework::instance().loadConfigFile("../../config.json.example"); + // drogon::HttpAppFramework::instance().loadConfigFile("../../config.json.example"); drogon::HttpAppFramework::instance().run(); - } diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 59effd0f..8f1f30c0 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -42,7 +42,7 @@ using namespace drogon; using namespace std::placeholders; -std::map> HttpApiBinderBase::_objMap; +std::map> HttpApiBinderBase::_objMap; std::mutex HttpApiBinderBase::_objMutex; static void godaemon(void) @@ -85,37 +85,39 @@ void HttpAppFrameworkImpl::enableDynamicViewsLoading(const std::vector &types) { - for(auto type : types) + for (auto type : types) { _fileTypeSet.insert(type); } } -void HttpAppFrameworkImpl::initRegex() { +void HttpAppFrameworkImpl::initRegex() +{ std::string regString; - for(auto &binder:_apiCtrlVector) + for (auto &binder : _apiCtrlVector) { std::regex reg("\\(\\[\\^/\\]\\*\\)"); - std::string tmp=std::regex_replace(binder.pathParameterPattern,reg,"[^/]*"); + std::string tmp = std::regex_replace(binder.pathParameterPattern, reg, "[^/]*"); regString.append("(").append(tmp).append(")|"); } - if(regString.length()>0) - regString.resize(regString.length()-1);//remove last '|' - LOG_TRACE<<"regex string:"< 0) + regString.resize(regString.length() - 1); //remove last '|' + LOG_TRACE << "regex string:" << regString; + _apiRegex = std::regex(regString); } void HttpAppFrameworkImpl::registerWebSocketController(const std::string &pathName, const std::string &ctrlName, @@ -124,14 +126,14 @@ void HttpAppFrameworkImpl::registerWebSocketController(const std::string &pathNa assert(!pathName.empty()); assert(!ctrlName.empty()); std::string path(pathName); - std::transform(pathName.begin(),pathName.end(),path.begin(),tolower); - auto objPtr=std::shared_ptr(DrClassMap::newObject(ctrlName)); - auto ctrlPtr=std::dynamic_pointer_cast(objPtr); + std::transform(pathName.begin(), pathName.end(), path.begin(), tolower); + auto objPtr = std::shared_ptr(DrClassMap::newObject(ctrlName)); + auto ctrlPtr = std::dynamic_pointer_cast(objPtr); assert(ctrlPtr); std::lock_guard guard(_websockCtrlMutex); - _websockCtrlMap[path].controller=ctrlPtr; - _websockCtrlMap[path].filtersName=filters; + _websockCtrlMap[path].controller = ctrlPtr; + _websockCtrlMap[path].filtersName = filters; } void HttpAppFrameworkImpl::registerHttpSimpleController(const std::string &pathName, const std::string &ctrlName, @@ -141,97 +143,96 @@ void HttpAppFrameworkImpl::registerHttpSimpleController(const std::string &pathN assert(!ctrlName.empty()); std::string path(pathName); - std::transform(pathName.begin(),pathName.end(),path.begin(),tolower); + std::transform(pathName.begin(), pathName.end(), path.begin(), tolower); std::lock_guard guard(_simpCtrlMutex); - _simpCtrlMap[path].controllerName=ctrlName; - _simpCtrlMap[path].filtersName=filters; + _simpCtrlMap[path].controllerName = ctrlName; + _simpCtrlMap[path].filtersName = filters; } void HttpAppFrameworkImpl::addApiPath(const std::string &path, - const HttpApiBinderBasePtr &binder, - const std::vector &filters) + const HttpApiBinderBasePtr &binder, + const std::vector &filters) { //path will be like /api/v1/service/method/{1}/{2}/xxx... std::vector places; - std::string tmpPath=path; - std::string paras=""; - std::regex regex=std::regex("\\{([0-9]+)\\}"); + std::string tmpPath = path; + std::string paras = ""; + std::regex regex = std::regex("\\{([0-9]+)\\}"); std::smatch results; - auto pos=tmpPath.find("?"); - if(pos!=std::string::npos) + auto pos = tmpPath.find("?"); + if (pos != std::string::npos) { - paras=tmpPath.substr(pos+1); - tmpPath=tmpPath.substr(0,pos); + paras = tmpPath.substr(pos + 1); + tmpPath = tmpPath.substr(0, pos); } - std::string originPath=tmpPath; - while(std::regex_search(tmpPath,results,regex)) + std::string originPath = tmpPath; + while (std::regex_search(tmpPath, results, regex)) { - if(results.size()>1) + if (results.size() > 1) { - size_t place=(size_t)std::stoi(results[1].str()); - if(place>binder->paramCount()||place==0) + size_t place = (size_t)std::stoi(results[1].str()); + if (place > binder->paramCount() || place == 0) { - LOG_ERROR<<"parameter placeholder(value="<paramCount()<<")"; + LOG_ERROR << "parameter placeholder(value=" << place << ") out of range (1 to " + << binder->paramCount() << ")"; exit(0); } places.push_back(place); } - tmpPath=results.suffix(); + tmpPath = results.suffix(); } - std::map parametersPlaces; - if(!paras.empty()) + std::map parametersPlaces; + if (!paras.empty()) { std::regex pregex("([^&]*)=\\{([0-9]+)\\}&*"); - while(std::regex_search(paras,results,pregex)) + while (std::regex_search(paras, results, pregex)) { - if(results.size()>2) + if (results.size() > 2) { - size_t place=(size_t)std::stoi(results[2].str()); - if(place>binder->paramCount()||place==0) + size_t place = (size_t)std::stoi(results[2].str()); + if (place > binder->paramCount() || place == 0) { - LOG_ERROR<<"parameter placeholder(value="<paramCount()<<")"; + LOG_ERROR << "parameter placeholder(value=" << place << ") out of range (1 to " + << binder->paramCount() << ")"; exit(0); } - parametersPlaces[results[1].str()]=place; + parametersPlaces[results[1].str()] = place; } - paras=results.suffix(); + paras = results.suffix(); } } struct ApiBinder _binder; - _binder.parameterPlaces=std::move(places); - _binder.queryParametersPlaces=std::move(parametersPlaces); - _binder.binderPtr=binder; - _binder.filtersName=filters; - _binder.pathParameterPattern=std::regex_replace(originPath,regex,"([^/]*)"); + _binder.parameterPlaces = std::move(places); + _binder.queryParametersPlaces = std::move(parametersPlaces); + _binder.binderPtr = binder; + _binder.filtersName = filters; + _binder.pathParameterPattern = std::regex_replace(originPath, regex, "([^/]*)"); std::lock_guard guard(_apiCtrlMutex); _apiCtrlVector.push_back(std::move(_binder)); } void HttpAppFrameworkImpl::registerHttpApiController(const std::string &pathPattern, - const HttpApiBinderBasePtr &binder, - const std::vector &filters) + const HttpApiBinderBasePtr &binder, + const std::vector &filters) { assert(!pathPattern.empty()); assert(binder); std::string path(pathPattern); - std::transform(path.begin(),path.end(),path.begin(),tolower); - addApiPath(path,binder,filters); - + std::transform(path.begin(), path.end(), path.begin(), tolower); + addApiPath(path, binder, filters); } void HttpAppFrameworkImpl::setThreadNum(size_t threadNum) { - assert(threadNum>=1); - _threadNum=threadNum; + assert(threadNum >= 1); + _threadNum = threadNum; } void HttpAppFrameworkImpl::setMaxConnectionNum(size_t maxConnections) { - _maxConnectionNum=maxConnections; + _maxConnectionNum = maxConnections; } void HttpAppFrameworkImpl::setMaxConnectionNumPerIP(size_t maxConnectionsPerIP) { - _maxConnectionNumPerIP=maxConnectionsPerIP; + _maxConnectionNumPerIP = maxConnectionsPerIP; } void HttpAppFrameworkImpl::loadConfigFile(const std::string &fileName) { @@ -242,53 +243,53 @@ void HttpAppFrameworkImpl::setLogPath(const std::string &logPath, const std::string &logfileBaseName, size_t logfileSize) { - if(logPath=="") + if (logPath == "") return; - if(access(logPath.c_str(),0)!=0) + if (access(logPath.c_str(), 0) != 0) { - std::cerr<<"Log path dose not exist!\n"; + std::cerr << "Log path dose not exist!\n"; exit(1); } - if(access(logPath.c_str(),R_OK|W_OK)!=0) + if (access(logPath.c_str(), R_OK | W_OK) != 0) { - std::cerr<<"Unable to access log path!\n"; + std::cerr << "Unable to access log path!\n"; exit(1); } - _logPath=logPath; - _logfileBaseName=logfileBaseName; - _logfileSize=logfileSize; + _logPath = logPath; + _logfileBaseName = logfileBaseName; + _logfileSize = logfileSize; } void HttpAppFrameworkImpl::setSSLFiles(const std::string &certPath, - const std::string &keyPath) + const std::string &keyPath) { - _sslCertPath=certPath; - _sslKeyPath=keyPath; + _sslCertPath = certPath; + _sslKeyPath = keyPath; } void HttpAppFrameworkImpl::addListener(const std::string &ip, uint16_t port, bool useSSL, - const std::string & certFile, - const std::string & keyFile) + 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"; - } + 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)); + _listeners.push_back(std::make_tuple(ip, port, useSSL, certFile, keyFile)); } void HttpAppFrameworkImpl::run() { // - LOG_INFO<<"Start to run..."; + LOG_INFO << "Start to run..."; trantor::AsyncFileLogger asyncFileLogger; - if(_runAsDaemon) + if (_runAsDaemon) { //go daemon! godaemon(); @@ -297,218 +298,213 @@ void HttpAppFrameworkImpl::run() #endif } //set relaunching - if(_relaunchOnError) + if (_relaunchOnError) { - while(true) + while (true) { int child_status = 0; auto child_pid = fork(); - if(child_pid < 0) + if (child_pid < 0) { - LOG_ERROR<<"fork error"; + LOG_ERROR << "fork error"; abort(); } - else if(child_pid == 0) + else if (child_pid == 0) { //child break; } waitpid(child_pid, &child_status, 0); sleep(5); - LOG_INFO<<"start new process"; + LOG_INFO << "start new process"; } } //set logger if (!_logPath.empty()) { - if (access(_logPath.c_str(), R_OK|W_OK) >= 0) + if (access(_logPath.c_str(), R_OK | W_OK) >= 0) { - std::string baseName=_logfileBaseName; - if(baseName=="") + std::string baseName = _logfileBaseName; + if (baseName == "") { - baseName="drogon"; + baseName = "drogon"; } - asyncFileLogger.setFileName(baseName,".log",_logPath); + asyncFileLogger.setFileName(baseName, ".log", _logPath); asyncFileLogger.startLogging(); - trantor::Logger::setOutputFunction([&](const char *msg,const uint64_t len){ - asyncFileLogger.output(msg,len); - },[&](){ - asyncFileLogger.flush(); - }); + trantor::Logger::setOutputFunction([&](const char *msg, const uint64_t len) { asyncFileLogger.output(msg, len); }, [&]() { asyncFileLogger.flush(); }); asyncFileLogger.setFileSizeLimit(_logfileSize); } else { - LOG_ERROR<<"log file path not exist"; + LOG_ERROR << "log file path not exist"; abort(); } } - if(_relaunchOnError) + if (_relaunchOnError) { - LOG_INFO<<"Start child process"; + LOG_INFO << "Start child process"; } //now start runing!! - _running=true; + _running = true; - if(!_libFilePaths.empty()) + if (!_libFilePaths.empty()) { - _sharedLibManagerPtr=std::unique_ptr(new SharedLibManager(&_loop,_libFilePaths)); - + _sharedLibManagerPtr = std::unique_ptr(new SharedLibManager(&_loop, _libFilePaths)); } std::vector> servers; std::vector> loopThreads; initRegex(); - for(auto listener:_listeners) + for (auto listener : _listeners) { - LOG_TRACE<<"thread num="<<_threadNum; + LOG_TRACE << "thread num=" << _threadNum; #ifdef __linux__ - for(size_t i=0;i<_threadNum;i++) + for (size_t i = 0; i < _threadNum; i++) { - auto loopThreadPtr=std::make_shared(); + auto loopThreadPtr = std::make_shared(); loopThreadPtr->run(); loopThreads.push_back(loopThreadPtr); - auto serverPtr=std::make_shared(loopThreadPtr->getLoop(), - InetAddress(std::get<0>(listener),std::get<1>(listener)),"drogon"); - if(std::get<2>(listener)) + auto serverPtr = std::make_shared(loopThreadPtr->getLoop(), + InetAddress(std::get<0>(listener), std::get<1>(listener)), "drogon"); + if (std::get<2>(listener)) { //enable ssl; #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"<enableSSL(cert,key); + 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->setConnectionCallback(std::bind(&HttpAppFrameworkImpl::onConnection,this,_1)); + serverPtr->setHttpAsyncCallback(std::bind(&HttpAppFrameworkImpl::onAsyncRequest, this, _1, _2)); + serverPtr->setConnectionCallback(std::bind(&HttpAppFrameworkImpl::onConnection, this, _1)); serverPtr->start(); servers.push_back(serverPtr); } #else - auto loopThreadPtr=std::make_shared(); + auto loopThreadPtr = std::make_shared(); loopThreadPtr->run(); loopThreads.push_back(loopThreadPtr); - auto serverPtr=std::make_shared(loopThreadPtr->getLoop(), - InetAddress(std::get<0>(listener),std::get<1>(listener)),"drogon"); - if(std::get<2>(listener)) + auto serverPtr = std::make_shared(loopThreadPtr->getLoop(), + InetAddress(std::get<0>(listener), std::get<1>(listener)), "drogon"); + if (std::get<2>(listener)) { //enable ssl; #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=="") + 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"<enableSSL(cert,key); + serverPtr->enableSSL(cert, key); #endif } serverPtr->setIoLoopNum(_threadNum); - serverPtr->setHttpAsyncCallback(std::bind(&HttpAppFrameworkImpl::onAsyncRequest,this,_1,_2)); - serverPtr->setNewWebsocketCallback(std::bind(&HttpAppFrameworkImpl::onNewWebsockRequest,this,_1,_2,_3)); - serverPtr->setWebsocketMessageCallback(std::bind(&HttpAppFrameworkImpl::onWebsockMessage,this,_1,_2)); - serverPtr->setDisconnectWebsocketCallback(std::bind(&HttpAppFrameworkImpl::onWebsockDisconnect,this,_1)); - serverPtr->setConnectionCallback(std::bind(&HttpAppFrameworkImpl::onConnection,this,_1)); + serverPtr->setHttpAsyncCallback(std::bind(&HttpAppFrameworkImpl::onAsyncRequest, this, _1, _2)); + serverPtr->setNewWebsocketCallback(std::bind(&HttpAppFrameworkImpl::onNewWebsockRequest, this, _1, _2, _3)); + serverPtr->setWebsocketMessageCallback(std::bind(&HttpAppFrameworkImpl::onWebsockMessage, this, _1, _2)); + serverPtr->setDisconnectWebsocketCallback(std::bind(&HttpAppFrameworkImpl::onWebsockDisconnect, this, _1)); + serverPtr->setConnectionCallback(std::bind(&HttpAppFrameworkImpl::onConnection, this, _1)); serverPtr->start(); servers.push_back(serverPtr); #endif } - if(_useSession) + if (_useSession) { - _sessionMapPtr=std::unique_ptr>(new CacheMap(&_loop)); + _sessionMapPtr = std::unique_ptr>(new CacheMap(&_loop)); } - _responseCacheMap=std::unique_ptr> - (new CacheMap(&_loop,1.0,4,50));//Max timeout up to about 70 days; - _loop.loop(); + _responseCacheMap = std::unique_ptr>(new CacheMap(&_loop, 1.0, 4, 50)); //Max timeout up to about 70 days; + _loop.loop(); } void HttpAppFrameworkImpl::doFilterChain(const std::shared_ptr>> &chain, - const HttpRequestPtr& req, - const std::function & callback, + const HttpRequestPtr &req, + const std::function &callback, bool needSetJsessionid, const std::string &session_id, - const std::function &missCallback) + const std::function &missCallback) { - if(chain->size()>0) { + if (chain->size() > 0) + { auto filter = chain->front(); chain->pop(); filter->doFilter(req, [=](HttpResponsePtr res) { if (needSetJsessionid) res->addCookie("JSESSIONID", session_id); - callback(res); - }, [=](){ - doFilterChain(chain,req,callback,needSetJsessionid,session_id,missCallback); - }); - }else{ + callback(res); }, [=]() { doFilterChain(chain, req, callback, needSetJsessionid, session_id, missCallback); }); + } + else + { missCallback(); } } void HttpAppFrameworkImpl::doFilters(const std::vector &filters, - const HttpRequestPtr& req, - const std::function & callback, + const HttpRequestPtr &req, + const std::function &callback, bool needSetJsessionid, const std::string &session_id, - const std::function &missCallback) + const std::function &missCallback) { - LOG_TRACE<<"filters count:"<>> filterPtrs= - std::make_shared>>(); - for(auto filter:filters) { + LOG_TRACE << "filters count:" << filters.size(); + std::shared_ptr>> filterPtrs = + std::make_shared>>(); + for (auto filter : filters) + { auto _object = std::shared_ptr(DrClassMap::newObject(filter)); auto _filter = std::dynamic_pointer_cast(_object); - if(_filter) + if (_filter) filterPtrs->push(_filter); - else { - LOG_ERROR<<"filter "<(wsConnPtr); + auto wsConnImplPtr = std::dynamic_pointer_cast(wsConnPtr); assert(wsConnImplPtr); - auto ctrl=wsConnImplPtr->controller(); - if(ctrl) + auto ctrl = wsConnImplPtr->controller(); + if (ctrl) { ctrl->handleConnectionClosed(wsConnPtr); wsConnImplPtr->setController(WebSocketControllerBasePtr()); } - } void HttpAppFrameworkImpl::onConnection(const TcpConnectionPtr &conn) { - if(conn->connected()) + if (conn->connected()) { - if(_connectionNum.fetch_add(1)>=_maxConnectionNum) + if (_connectionNum.fetch_add(1) >= _maxConnectionNum) { - LOG_ERROR<<"too much connections!force close!"; + LOG_ERROR << "too much connections!force close!"; conn->forceClose(); } - else if(_maxConnectionNumPerIP>0) + else if (_maxConnectionNumPerIP > 0) { { - auto iter=_connectionsNumMap.find(conn->peerAddr().toIp()); - if(iter==_connectionsNumMap.end()) + auto iter = _connectionsNumMap.find(conn->peerAddr().toIp()); + if (iter == _connectionsNumMap.end()) { - _connectionsNumMap[conn->peerAddr().toIp()]=0; + _connectionsNumMap[conn->peerAddr().toIp()] = 0; } - if(_connectionsNumMap[conn->peerAddr().toIp()]++>=_maxConnectionNumPerIP) + if (_connectionsNumMap[conn->peerAddr().toIp()]++ >= _maxConnectionNumPerIP) { conn->forceClose(); } @@ -519,7 +515,7 @@ void HttpAppFrameworkImpl::onConnection(const TcpConnectionPtr &conn) { _connectionNum--; - if(_maxConnectionNumPerIP>0&&_connectionsNumMap.find(conn->peerAddr().toIp())!=_connectionsNumMap.end()) + if (_maxConnectionNumPerIP > 0 && _connectionsNumMap.find(conn->peerAddr().toIp()) != _connectionsNumMap.end()) { std::lock_guard guard(_connectionsNumMapMutex); _connectionsNumMap[conn->peerAddr().toIp()]--; @@ -528,16 +524,17 @@ void HttpAppFrameworkImpl::onConnection(const TcpConnectionPtr &conn) } std::string parseWebsockFrame(trantor::MsgBuffer *buffer) { - if(buffer->readableBytes()>=2) + if (buffer->readableBytes() >= 2) { - auto secondByte=(*buffer)[1]; - size_t length=secondByte & 127; - int isMasked=(secondByte & 0x80); - if(isMasked!=0) + auto secondByte = (*buffer)[1]; + size_t length = secondByte & 127; + int isMasked = (secondByte & 0x80); + if (isMasked != 0) { - LOG_TRACE<<"data encoded!"; - } else - LOG_TRACE<<"plain data"; + LOG_TRACE << "data encoded!"; + } + else + LOG_TRACE << "plain data"; size_t indexFirstMask = 2; if (length == 126) @@ -548,46 +545,49 @@ std::string parseWebsockFrame(trantor::MsgBuffer *buffer) { indexFirstMask = 10; } - if(indexFirstMask>2&&buffer->readableBytes()>=indexFirstMask) + if (indexFirstMask > 2 && buffer->readableBytes() >= indexFirstMask) { - if(indexFirstMask==4) + if (indexFirstMask == 4) { - length=(unsigned char)(*buffer)[2]; - length=(length<<8)+(unsigned char)(*buffer)[3]; - LOG_TRACE<<"bytes[2]="<<(unsigned char)(*buffer)[2]; - LOG_TRACE<<"bytes[3]="<<(unsigned char)(*buffer)[3]; - } else if(indexFirstMask==10) + length = (unsigned char)(*buffer)[2]; + length = (length << 8) + (unsigned char)(*buffer)[3]; + LOG_TRACE << "bytes[2]=" << (unsigned char)(*buffer)[2]; + LOG_TRACE << "bytes[3]=" << (unsigned char)(*buffer)[3]; + } + else if (indexFirstMask == 10) + { + length = (unsigned char)(*buffer)[2]; + length = (length << 8) + (unsigned char)(*buffer)[3]; + length = (length << 8) + (unsigned char)(*buffer)[4]; + length = (length << 8) + (unsigned char)(*buffer)[5]; + length = (length << 8) + (unsigned char)(*buffer)[6]; + length = (length << 8) + (unsigned char)(*buffer)[7]; + length = (length << 8) + (unsigned char)(*buffer)[8]; + length = (length << 8) + (unsigned char)(*buffer)[9]; + // length=*((uint64_t *)(buffer->peek()+2)); + // length=ntohll(length); + } + else { - length=(unsigned char)(*buffer)[2]; - length=(length<<8)+(unsigned char)(*buffer)[3]; - length=(length<<8)+(unsigned char)(*buffer)[4]; - length=(length<<8)+(unsigned char)(*buffer)[5]; - length=(length<<8)+(unsigned char)(*buffer)[6]; - length=(length<<8)+(unsigned char)(*buffer)[7]; - length=(length<<8)+(unsigned char)(*buffer)[8]; - length=(length<<8)+(unsigned char)(*buffer)[9]; -// length=*((uint64_t *)(buffer->peek()+2)); -// length=ntohll(length); - } else{ assert(0); } } - LOG_TRACE<<"websocket message len="<readableBytes()>=(indexFirstMask+4+length)) + LOG_TRACE << "websocket message len=" << length; + if (buffer->readableBytes() >= (indexFirstMask + 4 + length)) { - auto masks=buffer->peek()+indexFirstMask; + auto masks = buffer->peek() + indexFirstMask; int indexFirstDataByte = indexFirstMask + 4; - auto rawData=buffer->peek()+indexFirstDataByte; + auto rawData = buffer->peek() + indexFirstDataByte; std::string message; message.resize(length); - LOG_TRACE<<"rawData[0]="<<(unsigned char)rawData[0]; - LOG_TRACE<<"masks[0]="<<(unsigned char)masks[0]; - for(size_t i=0;ihandleNewMessage(wsConnPtr,std::move(message)); + LOG_TRACE << "Got websock message:" << message; + ctrl->handleNewMessage(wsConnPtr, std::move(message)); } } } -void HttpAppFrameworkImpl::onNewWebsockRequest(const HttpRequestPtr& req, - const std::function & callback, +void HttpAppFrameworkImpl::onNewWebsockRequest(const HttpRequestPtr &req, + const std::function &callback, const WebSocketConnectionPtr &wsConnPtr) { - std::string wsKey=req->getHeader("Sec-WebSocket-Key"); - if(!wsKey.empty()) + std::string wsKey = req->getHeader("Sec-WebSocket-Key"); + if (!wsKey.empty()) { // magic="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; WebSocketControllerBasePtr ctrlPtr; std::vector filtersName; { std::string pathLower(req->path()); - std::transform(pathLower.begin(),pathLower.end(),pathLower.begin(),tolower); + std::transform(pathLower.begin(), pathLower.end(), pathLower.begin(), tolower); std::lock_guard guard(_websockCtrlMutex); - if(_websockCtrlMap.find(pathLower)!=_websockCtrlMap.end()) + if (_websockCtrlMap.find(pathLower) != _websockCtrlMap.end()) { - ctrlPtr=_websockCtrlMap[pathLower].controller; - filtersName=_websockCtrlMap[pathLower].filtersName; + ctrlPtr = _websockCtrlMap[pathLower].controller; + filtersName = _websockCtrlMap[pathLower].filtersName; } } - if(ctrlPtr) + if (ctrlPtr) { - doFilters(filtersName,req,callback,false,"",[=]() mutable - { + doFilters(filtersName, req, callback, false, "", [=]() mutable { wsKey.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); unsigned char accKey[SHA_DIGEST_LENGTH]; SHA1(reinterpret_cast(wsKey.c_str()), wsKey.length(), accKey); - auto base64Key=base64Encode(accKey,SHA_DIGEST_LENGTH); - auto resp=HttpResponse::newHttpResponse(); + auto base64Key = base64Encode(accKey, SHA_DIGEST_LENGTH); + auto resp = HttpResponse::newHttpResponse(); resp->setStatusCode(HttpResponse::k101SwitchingProtocols); - resp->addHeader("Upgrade","websocket"); - resp->addHeader("Connection","Upgrade"); - resp->addHeader("Sec-WebSocket-Accept",base64Key); + resp->addHeader("Upgrade", "websocket"); + resp->addHeader("Connection", "Upgrade"); + resp->addHeader("Sec-WebSocket-Accept", base64Key); callback(resp); - auto wsConnImplPtr=std::dynamic_pointer_cast(wsConnPtr); + auto wsConnImplPtr = std::dynamic_pointer_cast(wsConnPtr); assert(wsConnImplPtr); wsConnImplPtr->setController(ctrlPtr); - ctrlPtr->handleNewConnection(req,wsConnPtr); + ctrlPtr->handleNewConnection(req, wsConnPtr); return; }); return; } } - auto resp=drogon::HttpResponse::newNotFoundResponse(); + auto resp = drogon::HttpResponse::newNotFoundResponse(); resp->setCloseConnection(true); callback(resp); - } -void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr& req,const std::function & callback) +void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std::function &callback) { - LOG_TRACE << "new request:"<peerAddr().toIpPort()<<"->"<localAddr().toIpPort(); + LOG_TRACE << "new request:" << req->peerAddr().toIpPort() << "->" << req->localAddr().toIpPort(); LOG_TRACE << "Headers " << req->methodString() << " " << req->path(); #if 0 @@ -678,25 +676,24 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr& req,const std::f } #endif - LOG_TRACE << "http path=" << req->path(); - // LOG_TRACE << "query: " << req->query() ; + // LOG_TRACE << "query: " << req->query() ; - std::string session_id=req->getCookie("JSESSIONID"); - bool needSetJsessionid=false; - if(_useSession) + std::string session_id = req->getCookie("JSESSIONID"); + bool needSetJsessionid = false; + if (_useSession) { - if(session_id=="") + if (session_id == "") { - session_id=getuuid().c_str(); - needSetJsessionid=true; - _sessionMapPtr->insert(session_id,std::make_shared< Session >(),_sessionTimeout); + session_id = getuuid().c_str(); + needSetJsessionid = true; + _sessionMapPtr->insert(session_id, std::make_shared(), _sessionTimeout); } else { - if(_sessionMapPtr->find(session_id)==false) + if (_sessionMapPtr->find(session_id) == false) { - _sessionMapPtr->insert(session_id,std::make_shared< Session >(),_sessionTimeout); + _sessionMapPtr->insert(session_id, std::make_shared(), _sessionTimeout); } } (std::dynamic_pointer_cast(req))->setSession((*_sessionMapPtr)[session_id]); @@ -704,116 +701,141 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr& req,const std::f std::string path = req->path(); auto pos = path.rfind("."); - if(pos != std::string::npos) { + if (pos != std::string::npos) + { std::string filetype = path.substr(pos + 1); transform(filetype.begin(), filetype.end(), filetype.begin(), tolower); - if(_fileTypeSet.find(filetype) != _fileTypeSet.end()) { + if (_fileTypeSet.find(filetype) != _fileTypeSet.end()) + { LOG_INFO << "file query!"; std::string filePath = _rootPath + path; - std::shared_ptr resp=std::make_shared(); + std::shared_ptr resp = std::make_shared(); - if(needSetJsessionid) - resp->addCookie("JSESSIONID",session_id); + if (needSetJsessionid) + resp->addCookie("JSESSIONID", session_id); // pick a Content-Type for the file - if(filetype=="html") resp->setContentTypeCode(CT_TEXT_HTML); - else if(filetype=="js") resp->setContentTypeCode(CT_APPLICATION_X_JAVASCRIPT); - else if(filetype=="css") resp->setContentTypeCode(CT_TEXT_CSS); - else if(filetype=="xml") resp->setContentTypeCode(CT_TEXT_XML); - else if(filetype=="xsl") resp->setContentTypeCode(CT_TEXT_XSL); - else if(filetype=="txt") resp->setContentTypeCode(CT_TEXT_PLAIN); - else if(filetype=="svg") resp->setContentTypeCode(CT_IMAGE_SVG_XML); - else if(filetype=="ttf") resp->setContentTypeCode(CT_APPLICATION_X_FONT_TRUETYPE); - else if(filetype=="otf") resp->setContentTypeCode(CT_APPLICATION_X_FONT_OPENTYPE); - else if(filetype=="woff2")resp->setContentTypeCode(CT_APPLICATION_FONT_WOFF2); - else if(filetype=="woff") resp->setContentTypeCode(CT_APPLICATION_FONT_WOFF); - else if(filetype=="eot") resp->setContentTypeCode(CT_APPLICATION_VND_MS_FONTOBJ); - else if(filetype=="png") resp->setContentTypeCode(CT_IMAGE_PNG); - else if(filetype=="jpg") resp->setContentTypeCode(CT_IMAGE_JPG); - else if(filetype=="jpeg") resp->setContentTypeCode(CT_IMAGE_JPG); - else if(filetype=="gif") resp->setContentTypeCode(CT_IMAGE_GIF); - else if(filetype=="bmp") resp->setContentTypeCode(CT_IMAGE_BMP); - else if(filetype=="ico") resp->setContentTypeCode(CT_IMAGE_XICON); - else if(filetype=="icns") resp->setContentTypeCode(CT_IMAGE_ICNS); - else resp->setContentTypeCode(CT_APPLICATION_OCTET_STREAM); + if (filetype == "html") + resp->setContentTypeCode(CT_TEXT_HTML); + else if (filetype == "js") + resp->setContentTypeCode(CT_APPLICATION_X_JAVASCRIPT); + else if (filetype == "css") + resp->setContentTypeCode(CT_TEXT_CSS); + else if (filetype == "xml") + resp->setContentTypeCode(CT_TEXT_XML); + else if (filetype == "xsl") + resp->setContentTypeCode(CT_TEXT_XSL); + else if (filetype == "txt") + resp->setContentTypeCode(CT_TEXT_PLAIN); + else if (filetype == "svg") + resp->setContentTypeCode(CT_IMAGE_SVG_XML); + else if (filetype == "ttf") + resp->setContentTypeCode(CT_APPLICATION_X_FONT_TRUETYPE); + else if (filetype == "otf") + resp->setContentTypeCode(CT_APPLICATION_X_FONT_OPENTYPE); + else if (filetype == "woff2") + resp->setContentTypeCode(CT_APPLICATION_FONT_WOFF2); + else if (filetype == "woff") + resp->setContentTypeCode(CT_APPLICATION_FONT_WOFF); + else if (filetype == "eot") + resp->setContentTypeCode(CT_APPLICATION_VND_MS_FONTOBJ); + else if (filetype == "png") + resp->setContentTypeCode(CT_IMAGE_PNG); + else if (filetype == "jpg") + resp->setContentTypeCode(CT_IMAGE_JPG); + else if (filetype == "jpeg") + resp->setContentTypeCode(CT_IMAGE_JPG); + else if (filetype == "gif") + resp->setContentTypeCode(CT_IMAGE_GIF); + else if (filetype == "bmp") + resp->setContentTypeCode(CT_IMAGE_BMP); + else if (filetype == "ico") + resp->setContentTypeCode(CT_IMAGE_XICON); + else if (filetype == "icns") + resp->setContentTypeCode(CT_IMAGE_ICNS); + else + resp->setContentTypeCode(CT_APPLICATION_OCTET_STREAM); - readSendFile(filePath,req, resp); + readSendFile(filePath, req, resp); callback(resp); return; } - } - /*find simple controller*/ std::string pathLower(req->path()); - std::transform(pathLower.begin(),pathLower.end(),pathLower.begin(),tolower); + std::transform(pathLower.begin(), pathLower.end(), pathLower.begin(), tolower); - if(_simpCtrlMap.find(pathLower)!=_simpCtrlMap.end()) + if (_simpCtrlMap.find(pathLower) != _simpCtrlMap.end()) { - auto &filters=_simpCtrlMap[pathLower].filtersName; - doFilters(filters,req,callback,needSetJsessionid,session_id,[=](){ - auto &ctrlItem=_simpCtrlMap[pathLower]; + auto &filters = _simpCtrlMap[pathLower].filtersName; + doFilters(filters, req, callback, needSetJsessionid, session_id, [=]() { + auto &ctrlItem = _simpCtrlMap[pathLower]; const std::string &ctrlName = ctrlItem.controllerName; std::shared_ptr controller; HttpResponsePtr responsePtr; { //maybe update controller,so we use lock_guard to protect; std::lock_guard guard(ctrlItem._mutex); - controller=ctrlItem.controller; - responsePtr=ctrlItem.responsePtr.lock(); - if(!controller) + controller = ctrlItem.controller; + responsePtr = ctrlItem.responsePtr.lock(); + if (!controller) { auto _object = std::shared_ptr(DrClassMap::newObject(ctrlName)); controller = std::dynamic_pointer_cast(_object); - _simpCtrlMap[pathLower].controller=controller; + ctrlItem.controller = controller; } } - - if(controller) { - if(responsePtr) + if (controller) + { + if (responsePtr) { //use cached response! - LOG_TRACE<<"Use cached response"; - //make a copy response; - auto newResp=std::make_shared - (*std::dynamic_pointer_cast(responsePtr)); - if(!needSetJsessionid) - callback(newResp); + LOG_TRACE << "Use cached response"; + if (!needSetJsessionid) + callback(responsePtr); else { - newResp->addCookie("JSESSIONID",session_id); + //make a copy response; + auto newResp = std::make_shared(*std::dynamic_pointer_cast(responsePtr)); + newResp->setExpiredTime(-1); //make it temporary + newResp->addCookie("JSESSIONID", session_id); callback(newResp); } return; } else { - controller->asyncHandleHttpRequest(req, [=](const HttpResponsePtr& resp){ - auto newResp=resp; - if(resp->expiredTime()>=0) + controller->asyncHandleHttpRequest(req, [=](const HttpResponsePtr &resp) { + auto newResp = resp; + if (resp->expiredTime() >= 0) { //cache the response; + std::dynamic_pointer_cast(resp)->makeHeaderString(); { - std::lock_guard guard(_simpCtrlMap[pathLower]._mutex); - _responseCacheMap->insert(pathLower,resp,resp->expiredTime()); - _simpCtrlMap[pathLower].responsePtr=resp; + auto &item = _simpCtrlMap[pathLower]; + std::lock_guard guard(item._mutex); + _responseCacheMap->insert(pathLower, resp, resp->expiredTime()); + item.responsePtr = resp; } - //make a copy - newResp=std::make_shared - (*std::dynamic_pointer_cast(resp)); - } - if(needSetJsessionid) - newResp->addCookie("JSESSIONID",session_id); - callback(newResp); + if (needSetJsessionid) + { + //make a copy + newResp = std::make_shared(*std::dynamic_pointer_cast(resp)); + newResp->setExpiredTime(-1); //make it temporary + newResp->addCookie("JSESSIONID", session_id); + } + callback(newResp); }); } return; - } else { + } + else + { LOG_ERROR << "can't find controller " << ctrlName; } @@ -821,42 +843,42 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr& req,const std::f return; } //find api controller - if(_apiRegex.mark_count()>0) + if (_apiRegex.mark_count() > 0) { std::smatch result; - if(std::regex_match(req->path(),result,_apiRegex)) + if (std::regex_match(req->path(), result, _apiRegex)) { - for(size_t i=1;ipath()&&i<=_apiCtrlVector.size()) + if (result[i].str() == req->path() && i <= _apiCtrlVector.size()) { - size_t ctlIndex=i-1; - auto &binder=_apiCtrlVector[ctlIndex]; - LOG_TRACE<<"got api access,regex="< - (*std::dynamic_pointer_cast(responsePtr)); - if(!needSetJsessionid) - callback(newResp); + LOG_TRACE << "Use cached response"; + + if (!needSetJsessionid) + callback(responsePtr); else { - newResp->addCookie("JSESSIONID",session_id); + //make a copy response; + auto newResp = std::make_shared(*std::dynamic_pointer_cast(responsePtr)); + newResp->setExpiredTime(-1); //make it temporary + newResp->addCookie("JSESSIONID", session_id); callback(newResp); } return; @@ -864,143 +886,145 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr& req,const std::f std::vector params(binder.parameterPlaces.size()); std::smatch r; - if(std::regex_match(req->path(),r,std::regex(binder.pathParameterPattern))) + if (std::regex_match(req->path(), r, std::regex(binder.pathParameterPattern))) { - for(size_t j=1;jparams.size()) + size_t place = binder.parameterPlaces[j - 1]; + if (place > params.size()) params.resize(place); - params[place-1]=r[j].str(); - LOG_TRACE<<"place="<0) + if (binder.queryParametersPlaces.size() > 0) { - auto qureyPara=req->getParameters(); - for(auto parameter:qureyPara) + 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; - if(place>params.size()) + auto place = binder.queryParametersPlaces.find(parameter.first)->second; + if (place > params.size()) params.resize(place); - params[place-1]=parameter.second; + params[place - 1] = parameter.second; } } } std::list paraList; - for(auto p:params) + for (auto p : params) { - LOG_TRACE<handleHttpApiRequest(paraList,req,[=](const HttpResponsePtr& resp){ - LOG_TRACE<<"api resp:needSetJsessionid="<expiredTime()>=0) + binder.binderPtr->handleHttpApiRequest(paraList, req, [=](const HttpResponsePtr &resp) { + LOG_TRACE << "api resp:needSetJsessionid=" << needSetJsessionid << ";JSESSIONID=" << session_id; + auto newResp = resp; + if (resp->expiredTime() >= 0) { //cache the response; + std::dynamic_pointer_cast(resp)->makeHeaderString(); { - std::lock_guard guard(*(_apiCtrlVector[ctlIndex].binderMtx)); - _responseCacheMap->insert(_apiCtrlVector[ctlIndex].pathParameterPattern,resp,resp->expiredTime()); - _apiCtrlVector[ctlIndex].responsePtr=resp; + auto &binderIterm = _apiCtrlVector[ctlIndex]; + std::lock_guard guard(*(binderIterm.binderMtx)); + _responseCacheMap->insert(binderIterm.pathParameterPattern, resp, resp->expiredTime()); + binderIterm.responsePtr = resp; } - - //make a copy - newResp=std::make_shared - (*std::dynamic_pointer_cast(resp)); - } - - if(needSetJsessionid) - newResp->addCookie("JSESSIONID",session_id); + if (needSetJsessionid) + { + //make a copy + newResp = std::make_shared(*std::dynamic_pointer_cast(resp)); + newResp->setExpiredTime(-1); //make it temporary + newResp->addCookie("JSESSIONID", session_id); + } callback(newResp); - }); return; }); - } } } - else{ + else + { //No controller found - auto res=drogon::HttpResponse::newNotFoundResponse(); - if(needSetJsessionid) - res->addCookie("JSESSIONID",session_id); + auto res = drogon::HttpResponse::newNotFoundResponse(); + if (needSetJsessionid) + res->addCookie("JSESSIONID", session_id); callback(res); } } - else{ + else + { //No controller found - auto res=drogon::HttpResponse::newNotFoundResponse(); + auto res = drogon::HttpResponse::newNotFoundResponse(); - if(needSetJsessionid) - res->addCookie("JSESSIONID",session_id); + if (needSetJsessionid) + res->addCookie("JSESSIONID", session_id); callback(res); } } -void HttpAppFrameworkImpl::readSendFile(const std::string& filePath,const HttpRequestPtr& req, const HttpResponsePtr resp) +void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpRequestPtr &req, const HttpResponsePtr resp) { -//If-Modified-Since: Wed Jun 15 08:57:30 2016 GMT + //If-Modified-Since: Wed Jun 15 08:57:30 2016 GMT std::ifstream infile(filePath, std::ifstream::binary); LOG_INFO << "send http file:" << filePath; - if(!infile) { + if (!infile) + { resp->setStatusCode(HttpResponse::k404NotFound); resp->setCloseConnection(true); return; } - if(_enableLastModify) + if (_enableLastModify) { struct stat fileStat; - LOG_TRACE<<"enabled LastModify"; - if(stat(filePath.c_str(),&fileStat)>=0) + LOG_TRACE << "enabled LastModify"; + if (stat(filePath.c_str(), &fileStat) >= 0) { - LOG_TRACE<<"last modify time:"<getHeader("If-Modified-Since"); - if(modiStr!=""&&modiStr==lastModified) + std::string modiStr = req->getHeader("If-Modified-Since"); + if (modiStr != "" && modiStr == lastModified) { - LOG_TRACE<<"not Modified!"; + LOG_TRACE << "not Modified!"; resp->setStatusCode(HttpResponse::k304NotModified); return; } - resp->addHeader("Last-Modified",lastModified); + resp->addHeader("Last-Modified", lastModified); - resp->addHeader("Expires","Thu, 01 Jan 1970 00:00:00 GMT"); + resp->addHeader("Expires", "Thu, 01 Jan 1970 00:00:00 GMT"); } } - if(_useSendfile&& - (req->getHeader("Accept-Encoding").find("gzip")==std::string::npos|| - resp->getContentTypeCode()>=CT_APPLICATION_OCTET_STREAM)) + if (_useSendfile && + (req->getHeader("Accept-Encoding").find("gzip") == std::string::npos || + resp->getContentTypeCode() >= CT_APPLICATION_OCTET_STREAM)) { //binary file or no gzip supported by client std::dynamic_pointer_cast(resp)->setSendfile(filePath); } else { - std::streambuf * pbuf = infile.rdbuf(); - std::streamsize size = pbuf->pubseekoff(0,infile.end); - pbuf->pubseekoff(0,infile.beg); // rewind + std::streambuf *pbuf = infile.rdbuf(); + std::streamsize size = pbuf->pubseekoff(0, infile.end); + pbuf->pubseekoff(0, infile.beg); // rewind std::string str; str.resize(size); - pbuf->sgetn (&str[0],size); + pbuf->sgetn(&str[0], size); LOG_INFO << "file len:" << str.length(); resp->setBody(std::move(str)); } @@ -1013,16 +1037,12 @@ trantor::EventLoop *HttpAppFrameworkImpl::loop() return &_loop; } -HttpAppFramework& HttpAppFramework::instance() { +HttpAppFramework &HttpAppFramework::instance() +{ static HttpAppFrameworkImpl _instance; return _instance; } HttpAppFramework::~HttpAppFramework() { - } - - - - diff --git a/lib/src/HttpResponseImpl.cc b/lib/src/HttpResponseImpl.cc index cfd16a7b..5d0d6835 100755 --- a/lib/src/HttpResponseImpl.cc +++ b/lib/src/HttpResponseImpl.cc @@ -42,7 +42,7 @@ HttpResponsePtr HttpResponse::newHttpResponse() HttpResponsePtr HttpResponse::newHttpJsonResponse(const Json::Value &data) { - auto res=std::make_shared(); + auto res = std::make_shared(); res->setStatusCode(HttpResponse::k200OK); res->setContentTypeCode(CT_APPLICATION_JSON); Json::StreamWriterBuilder builder; @@ -54,8 +54,8 @@ HttpResponsePtr HttpResponse::newHttpJsonResponse(const Json::Value &data) HttpResponsePtr HttpResponse::newNotFoundResponse() { HttpViewData data; - data.insert("version",getVersion()); - auto res=HttpResponse::newHttpViewResponse("NotFound",data); + data.insert("version", getVersion()); + auto res = HttpResponse::newHttpViewResponse("NotFound", data); res->setStatusCode(HttpResponse::k404NotFound); //res->setCloseConnection(true); @@ -63,291 +63,302 @@ HttpResponsePtr HttpResponse::newNotFoundResponse() } HttpResponsePtr HttpResponse::newLocationRedirectResponse(const std::string &path) { - auto res=std::make_shared(); + auto res = std::make_shared(); res->setStatusCode(HttpResponse::k302Found); res->redirect(path.c_str()); return res; } -HttpResponsePtr HttpResponse::newHttpViewResponse(const std::string &viewName,const HttpViewData& data) +HttpResponsePtr HttpResponse::newHttpViewResponse(const std::string &viewName, const HttpViewData &data) { - return HttpViewBase::genHttpResponse(viewName,data); + return HttpViewBase::genHttpResponse(viewName, data); } -const std::string HttpResponseImpl::web_content_type_and_charset_to_string(uint8_t contenttype,const std::string &charSet) +const std::string HttpResponseImpl::web_content_type_and_charset_to_string(uint8_t contenttype, const std::string &charSet) { - switch(contenttype) { - case CT_TEXT_HTML: - return "text/html; charset="+charSet; + switch (contenttype) + { + case CT_TEXT_HTML: + return "text/html; charset=" + charSet; - case CT_APPLICATION_XML: - return "application/xml; charset="+charSet; + case CT_APPLICATION_XML: + return "application/xml; charset=" + charSet; - case CT_APPLICATION_JSON: - return "application/json; charset="+charSet; + case CT_APPLICATION_JSON: + return "application/json; charset=" + charSet; - case CT_APPLICATION_X_JAVASCRIPT: - return "application/x-javascript; charset="+charSet; + case CT_APPLICATION_X_JAVASCRIPT: + return "application/x-javascript; charset=" + charSet; - case CT_TEXT_CSS: - return "text/css; charset="+charSet; + case CT_TEXT_CSS: + return "text/css; charset=" + charSet; - case CT_TEXT_XML: - return "text/xml; charset="+charSet; + case CT_TEXT_XML: + return "text/xml; charset=" + charSet; - case CT_TEXT_XSL: - return "text/xsl; charset="+charSet; + case CT_TEXT_XSL: + return "text/xsl; charset=" + charSet; - case CT_APPLICATION_OCTET_STREAM: - return "application/octet-stream"; + case CT_APPLICATION_OCTET_STREAM: + return "application/octet-stream"; - case CT_IMAGE_SVG_XML: - return "image/svg+xml"; + case CT_IMAGE_SVG_XML: + return "image/svg+xml"; - case CT_APPLICATION_X_FONT_TRUETYPE: - return "application/x-font-truetype"; + case CT_APPLICATION_X_FONT_TRUETYPE: + return "application/x-font-truetype"; - case CT_APPLICATION_X_FONT_OPENTYPE: - return "application/x-font-opentype"; + case CT_APPLICATION_X_FONT_OPENTYPE: + return "application/x-font-opentype"; - case CT_APPLICATION_FONT_WOFF: - return "application/font-woff"; + case CT_APPLICATION_FONT_WOFF: + return "application/font-woff"; - case CT_APPLICATION_FONT_WOFF2: - return "application/font-woff2"; + case CT_APPLICATION_FONT_WOFF2: + return "application/font-woff2"; - case CT_APPLICATION_VND_MS_FONTOBJ: - return "application/vnd.ms-fontobject"; + case CT_APPLICATION_VND_MS_FONTOBJ: + return "application/vnd.ms-fontobject"; - case CT_IMAGE_PNG: - return "image/png"; + case CT_IMAGE_PNG: + return "image/png"; - case CT_IMAGE_JPG: - return "image/jpeg"; + case CT_IMAGE_JPG: + return "image/jpeg"; - case CT_IMAGE_GIF: - return "image/gif"; + case CT_IMAGE_GIF: + return "image/gif"; - case CT_IMAGE_XICON: - return "image/x-icon"; + case CT_IMAGE_XICON: + return "image/x-icon"; - case CT_IMAGE_BMP: - return "image/bmp"; + case CT_IMAGE_BMP: + return "image/bmp"; - case CT_IMAGE_ICNS: - return "image/icns"; + case CT_IMAGE_ICNS: + return "image/icns"; - default: - case CT_TEXT_PLAIN: - return "text/plain; charset="+charSet; + default: + case CT_TEXT_PLAIN: + return "text/plain; charset=" + charSet; } } std::string HttpResponseImpl::web_content_type_to_string(uint8_t contenttype) { - switch(contenttype) { - case CT_TEXT_HTML: - return "text/html; charset=utf-8"; + switch (contenttype) + { + case CT_TEXT_HTML: + return "text/html; charset=utf-8"; - case CT_APPLICATION_XML: - return "application/xml; charset=utf-8"; + case CT_APPLICATION_XML: + return "application/xml; charset=utf-8"; - case CT_APPLICATION_JSON: - return "application/json; charset=utf-8"; + case CT_APPLICATION_JSON: + return "application/json; charset=utf-8"; - case CT_APPLICATION_X_JAVASCRIPT: - return "application/x-javascript; charset=utf-8"; + case CT_APPLICATION_X_JAVASCRIPT: + return "application/x-javascript; charset=utf-8"; - case CT_TEXT_CSS: - return "text/css; charset=utf-8"; + case CT_TEXT_CSS: + return "text/css; charset=utf-8"; - case CT_TEXT_XML: - return "text/xml; charset=utf-8"; + case CT_TEXT_XML: + return "text/xml; charset=utf-8"; - case CT_TEXT_XSL: - return "text/xsl; charset=utf-8"; + case CT_TEXT_XSL: + return "text/xsl; charset=utf-8"; - case CT_APPLICATION_OCTET_STREAM: - return "application/octet-stream"; + case CT_APPLICATION_OCTET_STREAM: + return "application/octet-stream"; - case CT_IMAGE_SVG_XML: - return "image/svg+xml"; + case CT_IMAGE_SVG_XML: + return "image/svg+xml"; - case CT_APPLICATION_X_FONT_TRUETYPE: - return "application/x-font-truetype"; + case CT_APPLICATION_X_FONT_TRUETYPE: + return "application/x-font-truetype"; - case CT_APPLICATION_X_FONT_OPENTYPE: - return "application/x-font-opentype"; + case CT_APPLICATION_X_FONT_OPENTYPE: + return "application/x-font-opentype"; - case CT_APPLICATION_FONT_WOFF: - return "application/font-woff"; + case CT_APPLICATION_FONT_WOFF: + return "application/font-woff"; - case CT_APPLICATION_FONT_WOFF2: - return "application/font-woff2"; + case CT_APPLICATION_FONT_WOFF2: + return "application/font-woff2"; - case CT_APPLICATION_VND_MS_FONTOBJ: - return "application/vnd.ms-fontobject"; + case CT_APPLICATION_VND_MS_FONTOBJ: + return "application/vnd.ms-fontobject"; - case CT_IMAGE_PNG: - return "image/png"; + case CT_IMAGE_PNG: + return "image/png"; - case CT_IMAGE_JPG: - return "image/jpeg"; + case CT_IMAGE_JPG: + return "image/jpeg"; - case CT_IMAGE_GIF: - return "image/gif"; + case CT_IMAGE_GIF: + return "image/gif"; - case CT_IMAGE_XICON: - return "image/x-icon"; + case CT_IMAGE_XICON: + return "image/x-icon"; - case CT_IMAGE_BMP: - return "image/bmp"; + case CT_IMAGE_BMP: + return "image/bmp"; - case CT_IMAGE_ICNS: - return "image/icns"; + case CT_IMAGE_ICNS: + return "image/icns"; - default: - case CT_TEXT_PLAIN: - return "text/plain; charset=utf-8"; + default: + case CT_TEXT_PLAIN: + return "text/plain; charset=utf-8"; } } std::string HttpResponseImpl::web_response_code_to_string(int code) { - switch(code) { - case 100: - return "Continue"; - case 101: - return "Switching Protocols"; - case 200: - return "OK"; - case 201: - return "Created"; - case 202: - return "Accepted"; - case 203: - return "Non-Authoritative Information"; - case 204: - return "No Content"; - case 205: - return "Reset Content"; - case 206: - return "Partial Content"; - case 300: - return "Multiple Choices"; - case 301: - return "Moved Permanently"; - case 302: - return "Found"; - case 303: - return "See Other"; - case 304: - return "Not Modified"; - case 305: - return "Use Proxy"; - case 307: - return "Temporary Redirect"; - case 400: + switch (code) + { + case 100: + return "Continue"; + case 101: + return "Switching Protocols"; + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 206: + return "Partial Content"; + case 300: + return "Multiple Choices"; + case 301: + return "Moved Permanently"; + case 302: + return "Found"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 307: + return "Temporary Redirect"; + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 402: + return "Payment Required"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Time-out"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Request Entity Too Large"; + case 414: + return "Request-URI Too Large"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Requested range not satisfiable"; + case 417: + return "Expectation Failed"; + case 500: + return "Internal Server Error"; + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Time-out"; + case 505: + return "HTTP Version not supported"; + default: + if (code >= 100 && code < 200) + return "Informational"; + + if (code >= 200 && code < 300) + return "Successful"; + + if (code >= 300 && code < 400) + return "Redirection"; + + if (code >= 400 && code < 500) return "Bad Request"; - case 401: - return "Unauthorized"; - case 402: - return "Payment Required"; - case 403: - return "Forbidden"; - case 404: - return "Not Found"; - case 405: - return "Method Not Allowed"; - case 406: - return "Not Acceptable"; - case 407: - return "Proxy Authentication Required"; - case 408: - return "Request Time-out"; - case 409: - return "Conflict"; - case 410: - return "Gone"; - case 411: - return "Length Required"; - case 412: - return "Precondition Failed"; - case 413: - return "Request Entity Too Large"; - case 414: - return "Request-URI Too Large"; - case 415: - return "Unsupported Media Type"; - case 416: - return "Requested range not satisfiable"; - case 417: - return "Expectation Failed"; - case 500: - return "Internal Server Error"; - case 501: - return "Not Implemented"; - case 502: - return "Bad Gateway"; - case 503: - return "Service Unavailable"; - case 504: - return "Gateway Time-out"; - case 505: - return "HTTP Version not supported"; - default: - if(code >= 100 && code < 200) - return "Informational"; - if(code >= 200 && code < 300) - return "Successful"; + if (code >= 500 && code < 600) + return "Server Error"; - if(code >= 300 && code < 400) - return "Redirection"; - - if(code >= 400 && code < 500) - return "Bad Request"; - - if(code >= 500 && code < 600) - return "Server Error"; - - return "Undefined Error"; + return "Undefined Error"; } } -void HttpResponseImpl::makeHeaderString(MsgBuffer* output) const +void HttpResponseImpl::makeHeaderString(MsgBuffer *output) const { char buf[64]; snprintf(buf, sizeof buf, "HTTP/1.1 %d ", _statusCode); output->append(buf); output->append(_statusMessage); output->append("\r\n"); - if (_sendfileName.empty()) { + if (_sendfileName.empty()) + { snprintf(buf, sizeof buf, "Content-Length: %lu\r\n", _body.size()); - } else { + } + else + { struct stat filestat; - if (stat(_sendfileName.c_str(), &filestat) < 0) { + if (stat(_sendfileName.c_str(), &filestat) < 0) + { LOG_SYSERR << _sendfileName << " stat error"; return; } #ifdef __linux__ - snprintf(buf, sizeof buf, "Content-Length: %ld\r\n",filestat.st_size); + snprintf(buf, sizeof buf, "Content-Length: %ld\r\n", filestat.st_size); #else snprintf(buf, sizeof buf, "Content-Length: %lld\r\n", filestat.st_size); #endif } output->append(buf); - if (_headers.find("Connection") == _headers.end()) { - if (_closeConnection) { + if (_headers.find("Connection") == _headers.end()) + { + if (_closeConnection) + { output->append("Connection: close\r\n"); - } else { + } + else + { //output->append("Connection: Keep-Alive\r\n"); } } - for (auto it = _headers.begin(); it != _headers.end(); - ++it) { + ++it) + { output->append(it->first); output->append(": "); output->append(it->second); @@ -357,24 +368,24 @@ void HttpResponseImpl::makeHeaderString(MsgBuffer* output) const output->append("Server: drogon/"); output->append(drogon::getVersion()); output->append("\r\n"); - - if(_expriedTime>=0) - _fullHeaderString=std::string(output->peek(),output->readableBytes()); } -void HttpResponseImpl::appendToBuffer(MsgBuffer* output) const { - if(_fullHeaderString.empty()) +void HttpResponseImpl::appendToBuffer(MsgBuffer *output) const +{ + if (!_fullHeaderString) { makeHeaderString(output); } else { - output->append(_fullHeaderString); + output->append(*_fullHeaderString); } //output cookies - if(_cookies.size() > 0) { - for(auto it = _cookies.begin(); it != _cookies.end(); it++) { + if (_cookies.size() > 0) + { + for (auto it = _cookies.begin(); it != _cookies.end(); it++) + { output->append(it->second.cookieString()); } @@ -385,7 +396,6 @@ void HttpResponseImpl::appendToBuffer(MsgBuffer* output) const { output->append(getHttpFullDate(trantor::Date::date())); output->append("\r\n\r\n"); - LOG_TRACE<<"reponse(no body):"<peek(); - output->append(_body); - + LOG_TRACE << "reponse(no body):" << output->peek(); + output->append(_body); } diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index 8e4efcb3..a9eb70c6 100755 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -8,7 +8,6 @@ // // This is a public header file, it must only include public header files. - //taken from muduo and modified /** @@ -25,8 +24,6 @@ * */ - - #pragma once #include @@ -36,305 +33,321 @@ #include #include #include +#include using namespace trantor; namespace drogon { - class HttpResponseImpl:public HttpResponse +class HttpResponseImpl : public HttpResponse +{ + friend class HttpContext; + + public: + explicit HttpResponseImpl() + : _statusCode(kUnknown), + _closeConnection(false), + _left_body_length(0), + _current_chunk_length(0) { - friend class HttpContext; - public: + } + virtual HttpStatusCode statusCode() override + { + return _statusCode; + } + virtual void setStatusCode(HttpStatusCode code) override + { + _statusCode = code; + setStatusMessage(web_response_code_to_string(code)); + } - explicit HttpResponseImpl() - : _statusCode(kUnknown), - _closeConnection(false), - _left_body_length(0), - _current_chunk_length(0) - { + virtual void setStatusCode(HttpStatusCode code, const std::string &status_message) override + { + _statusCode = code; + setStatusMessage(status_message); + } - } - virtual HttpStatusCode statusCode() override - { - return _statusCode; - } - virtual void setStatusCode(HttpStatusCode code) override - { - _statusCode = code; - setStatusMessage(web_response_code_to_string(code)); - } + virtual void setVersion(const Version v) override + { + _v = v; + } - virtual void setStatusCode(HttpStatusCode code, const std::string& status_message) override - { - _statusCode = code; - setStatusMessage(status_message); - } + virtual void setCloseConnection(bool on) override + { + _closeConnection = on; + } - virtual void setVersion(const Version v) override - { - _v = v; - } + virtual bool closeConnection() const override + { + return _closeConnection; + } + virtual void setContentTypeCode(uint8_t type) override + { + _contentType = type; + setContentType(web_content_type_to_string(type)); + } - virtual void setCloseConnection(bool on) override - { - _closeConnection = on; - } + virtual void setContentTypeCodeAndCharacterSet(uint8_t type, const std::string charSet = "utf-8") override + { + _contentType = type; + setContentType(web_content_type_and_charset_to_string(type, charSet)); + } - virtual bool closeConnection() const override - { - return _closeConnection; - } + virtual uint8_t getContentTypeCode() override + { + return _contentType; + } + // virtual uint8_t contentTypeCode() override + // { + // return _contentType; + // } - virtual void setContentTypeCode(uint8_t type) override + virtual std::string getHeader(const std::string &key) const override + { + auto field = key; + transform(field.begin(), field.end(), field.begin(), ::tolower); + auto iter = _headers.find(field); + if (iter == _headers.end()) { - _contentType=type; - setContentType(web_content_type_to_string(type)); + return ""; } + else + { + return iter->second; + } + } + virtual void addHeader(const std::string &key, const std::string &value) override + { + _fullHeaderString.reset(); + auto field = key; + transform(field.begin(), field.end(), field.begin(), ::tolower); + _headers[field] = value; + } - virtual void setContentTypeCodeAndCharacterSet(uint8_t type,const std::string charSet="utf-8") override - { - _contentType=type; - setContentType(web_content_type_and_charset_to_string(type,charSet)); - } + virtual void addHeader(const std::string &key, std::string &&value) override + { + _fullHeaderString.reset(); + auto field = key; + transform(field.begin(), field.end(), field.begin(), ::tolower); + _headers[field] = std::move(value); + } - virtual uint8_t getContentTypeCode() override + virtual void addHeader(const char *start, const char *colon, const char *end) override + { + _fullHeaderString.reset(); + std::string field(start, colon); + transform(field.begin(), field.end(), field.begin(), ::tolower); + ++colon; + while (colon < end && isspace(*colon)) { - return _contentType; - } -// virtual uint8_t contentTypeCode() override -// { -// return _contentType; -// } - - virtual std::string getHeader(const std::string& key) const override - { - auto field=key; - transform(field.begin(), field.end(), field.begin(), ::tolower); - auto iter=_headers.find(field); - if(iter == _headers.end()) - { - return ""; - } - else - { - return iter->second; - } - } - virtual void addHeader(const std::string& key, const std::string& value) override - { - auto field=key; - transform(field.begin(), field.end(), field.begin(), ::tolower); - _headers[field] = value; - } - - virtual void addHeader(const std::string& key, std::string&& value) override - { - auto field=key; - transform(field.begin(), field.end(), field.begin(), ::tolower); - _headers[field] = std::move(value); - } - - virtual void addHeader(const char* start, const char* colon, const char* end) override - { - std::string field(start, colon); - transform(field.begin(), field.end(), field.begin(), ::tolower); ++colon; - while (colon < end && isspace(*colon)) { - ++colon; - } - std::string value(colon, end); - while (!value.empty() && isspace(value[value.size() - 1])) { - value.resize(value.size() - 1); - } - _headers[field] = value; + } + std::string value(colon, end); + while (!value.empty() && isspace(value[value.size() - 1])) + { + value.resize(value.size() - 1); + } + _headers[field] = value; - //FIXME:reponse cookie should be "Set-Cookie:...." - if(field == "cookie") { - //LOG_INFO<<"cookies!!!:"<0) - { - std::string &coo = value; - auto epos = coo.find("="); - if(epos != std::string::npos) { - std::string cookie_name = coo.substr(0, epos); - std::string::size_type cpos=0; - while(cpos(); - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - JSONCPP_STRING errs; - std::unique_ptr reader(builder.newCharReader()); - if (!reader->parse(_body.data(), _body.data() + _body.size(), _jsonPtr.get() , &errs)) + //LOG_INFO<<"cookies!!!:"< 0) + { + std::string &coo = value; + auto epos = coo.find("="); + if (epos != std::string::npos) + { + std::string cookie_name = coo.substr(0, epos); + std::string::size_type cpos = 0; + while (cpos < cookie_name.length() && isspace(cookie_name[cpos])) + cpos++; + cookie_name = cookie_name.substr(cpos); + std::string cookie_value = coo.substr(epos + 1); + _cookies.insert(std::make_pair(cookie_name, Cookie(cookie_name, cookie_value))); + } } } - virtual const std::shared_ptr getJsonObject() const override + } + + virtual void addCookie(const std::string &key, const std::string &value) override + { + _cookies.insert(std::make_pair(key, Cookie(key, value))); + } + + virtual void addCookie(const Cookie &cookie) override + { + _cookies.insert(std::make_pair(cookie.key(), cookie)); + } + + virtual void removeCookie(const std::string &key) override + { + _cookies.erase(key); + } + + virtual void setBody(const std::string &body) override + { + _body = body; + } + virtual void setBody(std::string &&body) override + { + _body = std::move(body); + } + + virtual void redirect(const std::string &url) override + { + _headers["Location"] = url; + } + void appendToBuffer(MsgBuffer *output) const; + + virtual void clear() override + { + _statusCode = kUnknown; + _v = kHttp11; + _statusMessage.clear(); + _fullHeaderString.reset(); + _headers.clear(); + _cookies.clear(); + _body.clear(); + _left_body_length = 0; + _current_chunk_length = 0; + } + + virtual void setExpiredTime(ssize_t expiredTime) override + { + _expriedTime = expiredTime; + } + + virtual ssize_t expiredTime() const override { return _expriedTime; } + + // void setReceiveTime(trantor::Date t) + // { + // receiveTime_ = t; + // } + + virtual const std::string &getBody() const override + { + return _body; + } + virtual std::string &getBody() override + { + return _body; + } + void swap(HttpResponseImpl &that) + { + _headers.swap(that._headers); + _cookies.swap(that._cookies); + std::swap(_statusCode, that._statusCode); + std::swap(_v, that._v); + _statusMessage.swap(that._statusMessage); + std::swap(_closeConnection, that._closeConnection); + _body.swap(that._body); + std::swap(_left_body_length, that._left_body_length); + std::swap(_current_chunk_length, that._current_chunk_length); + std::swap(_contentType, that._contentType); + _jsonPtr.swap(that._jsonPtr); + _fullHeaderString.swap(that._fullHeaderString); + } + void parseJson() const + { + //parse json data in reponse + _jsonPtr = std::make_shared(); + Json::CharReaderBuilder builder; + builder["collectComments"] = false; + JSONCPP_STRING errs; + std::unique_ptr reader(builder.newCharReader()); + if (!reader->parse(_body.data(), _body.data() + _body.size(), _jsonPtr.get(), &errs)) { - if(!_jsonPtr){ - parseJson(); - } - return _jsonPtr; + LOG_ERROR << errs; + _jsonPtr.reset(); } - const std::string &sendfileName() const { - return _sendfileName; - } - void setSendfile(const std::string &filename){ - _sendfileName=filename; - } - protected: - static std::string web_content_type_to_string(uint8_t contenttype); - static const std::string web_content_type_and_charset_to_string(uint8_t contenttype, - const std::string &charSet); - - static std::string web_response_code_to_string(int code); - - void makeHeaderString(MsgBuffer* output) const; - - private: - std::map _headers; - std::map _cookies; - HttpStatusCode _statusCode; - // FIXME: add http version - Version _v; - std::string _statusMessage; - bool _closeConnection; - std::string _body; - size_t _left_body_length; - size_t _current_chunk_length; - uint8_t _contentType=CT_TEXT_HTML; - - ssize_t _expriedTime=-1; - std::string _sendfileName; - mutable std::shared_ptr _jsonPtr; - - mutable std::string _fullHeaderString; - //trantor::Date receiveTime_; - - void setContentType(const std::string& contentType) + } + virtual const std::shared_ptr getJsonObject() const override + { + if (!_jsonPtr) { - addHeader("Content-Type", contentType); + parseJson(); } - void setContentType(std::string && contentType) - { - addHeader("Content-Type", std::move(contentType)); - } - void setStatusMessage(const std::string& message) - { - _statusMessage = message; - } - void setStatusMessage(std::string && message) - { - _statusMessage = std::move(message); - } - }; - typedef std::shared_ptr HttpResponseImplPtr; -} + return _jsonPtr; + } + const std::string &sendfileName() const + { + return _sendfileName; + } + void setSendfile(const std::string &filename) + { + _sendfileName = filename; + } + void makeHeaderString() + { + trantor::MsgBuffer buf; + makeHeaderString(&buf); + _fullHeaderString = std::make_shared(buf.peek(), buf.readableBytes()); + } + protected: + static std::string web_content_type_to_string(uint8_t contenttype); + static const std::string web_content_type_and_charset_to_string(uint8_t contenttype, + const std::string &charSet); + static std::string web_response_code_to_string(int code); + + void makeHeaderString(MsgBuffer *output) const; + + private: + std::map _headers; + std::map _cookies; + HttpStatusCode _statusCode; + // FIXME: add http version + Version _v; + std::string _statusMessage; + bool _closeConnection; + std::string _body; + size_t _left_body_length; + size_t _current_chunk_length; + uint8_t _contentType = CT_TEXT_HTML; + + ssize_t _expriedTime = -1; + std::string _sendfileName; + mutable std::shared_ptr _jsonPtr; + + std::shared_ptr _fullHeaderString; + //trantor::Date receiveTime_; + + void setContentType(const std::string &contentType) + { + addHeader("Content-Type", contentType); + } + void setContentType(std::string &&contentType) + { + addHeader("Content-Type", std::move(contentType)); + } + void setStatusMessage(const std::string &message) + { + _statusMessage = message; + } + void setStatusMessage(std::string &&message) + { + _statusMessage = std::move(message); + } +}; +typedef std::shared_ptr HttpResponseImplPtr; +} // namespace drogon diff --git a/lib/src/HttpServer.cc b/lib/src/HttpServer.cc index f9298cd2..902bb293 100755 --- a/lib/src/HttpServer.cc +++ b/lib/src/HttpServer.cc @@ -35,41 +35,39 @@ using namespace std::placeholders; using namespace drogon; using namespace trantor; - -static void defaultHttpAsyncCallback(const HttpRequestPtr&,const std::function & callback) +static void defaultHttpAsyncCallback(const HttpRequestPtr &, const std::function &callback) { - auto resp=HttpResponse::newNotFoundResponse(); + auto resp = HttpResponse::newNotFoundResponse(); resp->setCloseConnection(true); callback(resp); } -static void defaultWebSockAsyncCallback(const HttpRequestPtr&, - const std::function & callback, - const WebSocketConnectionPtr& wsConnPtr) +static void defaultWebSockAsyncCallback(const HttpRequestPtr &, + const std::function &callback, + const WebSocketConnectionPtr &wsConnPtr) { - auto resp=HttpResponse::newNotFoundResponse(); + auto resp = HttpResponse::newNotFoundResponse(); resp->setCloseConnection(true); callback(resp); } - -static void defaultConnectionCallback(const trantor::TcpConnectionPtr & conn) +static void defaultConnectionCallback(const trantor::TcpConnectionPtr &conn) { return; } -HttpServer::HttpServer(EventLoop* loop, - const InetAddress& listenAddr, - const std::string& name) - : server_(loop, listenAddr, name.c_str()), - httpAsyncCallback_(defaultHttpAsyncCallback), - newWebsocketCallback_(defaultWebSockAsyncCallback), - _connectionCallback(defaultConnectionCallback) +HttpServer::HttpServer(EventLoop *loop, + const InetAddress &listenAddr, + const std::string &name) + : server_(loop, listenAddr, name.c_str()), + httpAsyncCallback_(defaultHttpAsyncCallback), + newWebsocketCallback_(defaultWebSockAsyncCallback), + _connectionCallback(defaultConnectionCallback) { server_.setConnectionCallback( - std::bind(&HttpServer::onConnection, this, _1)); + std::bind(&HttpServer::onConnection, this, _1)); server_.setRecvMessageCallback( - std::bind(&HttpServer::onMessage, this, _1, _2)); + std::bind(&HttpServer::onMessage, this, _1, _2)); } HttpServer::~HttpServer() @@ -83,18 +81,19 @@ void HttpServer::start() server_.start(); } -void HttpServer::onConnection(const TcpConnectionPtr& conn) +void HttpServer::onConnection(const TcpConnectionPtr &conn) { - if (conn->connected()) { + if (conn->connected()) + { conn->setContext(HttpContext(conn)); } - else if(conn->disconnected()) + else if (conn->disconnected()) { - LOG_TRACE<<"conn disconnected!"; - HttpContext* context = any_cast(conn->getMutableContext()); + LOG_TRACE << "conn disconnected!"; + HttpContext *context = any_cast(conn->getMutableContext()); // LOG_INFO << "###:" << string(buf->peek(), buf->readableBytes()); - if(context->webSocketConn()) + if (context->webSocketConn()) { disconnectWebsocketCallback_(context->webSocketConn()); } @@ -103,107 +102,113 @@ void HttpServer::onConnection(const TcpConnectionPtr& conn) _connectionCallback(conn); } -void HttpServer::onMessage(const TcpConnectionPtr& conn, - MsgBuffer* buf) +void HttpServer::onMessage(const TcpConnectionPtr &conn, + MsgBuffer *buf) { - HttpContext* context = any_cast(conn->getMutableContext()); + HttpContext *context = any_cast(conn->getMutableContext()); // LOG_INFO << "###:" << string(buf->peek(), buf->readableBytes()); - if(context->webSocketConn()) + if (context->webSocketConn()) { //websocket payload,we shouldn't parse it - webSocketMessageCallback_(context->webSocketConn(),buf); + webSocketMessageCallback_(context->webSocketConn(), buf); return; } - if (!context->parseRequest(buf)) { + if (!context->parseRequest(buf)) + { conn->send("HTTP/1.1 400 Bad Request\r\n\r\n"); //conn->shutdown(); } - if (context->gotAll()) { + if (context->gotAll()) + { context->requestImpl()->parsePremeter(); context->requestImpl()->setPeerAddr(conn->peerAddr()); context->requestImpl()->setLocalAddr(conn->localAddr()); context->requestImpl()->setReceiveDate(trantor::Date::date()); - if(context->firstReq()&&isWebSocket(conn,context->request())) + if (context->firstReq() && isWebSocket(conn, context->request())) { - auto wsConn=std::make_shared(conn); - newWebsocketCallback_(context->request(),[=](const HttpResponsePtr &resp) mutable - { - if(resp->statusCode()==HttpResponse::k101SwitchingProtocols) + auto wsConn = std::make_shared(conn); + newWebsocketCallback_(context->request(), [=](const HttpResponsePtr &resp) mutable { + if (resp->statusCode() == HttpResponse::k101SwitchingProtocols) { context->setWebsockConnection(wsConn); } MsgBuffer buffer; std::dynamic_pointer_cast(resp)->appendToBuffer(&buffer); conn->send(std::move(buffer)); - },wsConn); + }, + wsConn); } else onRequest(conn, context->request()); context->reset(); } } -bool HttpServer::isWebSocket(const TcpConnectionPtr& conn, const HttpRequestPtr& req) +bool HttpServer::isWebSocket(const TcpConnectionPtr &conn, const HttpRequestPtr &req) { - if(req->getHeader("Connection")=="Upgrade"&& - req->getHeader("Upgrade")=="websocket") + if (req->getHeader("Connection") == "Upgrade" && + req->getHeader("Upgrade") == "websocket") { - LOG_TRACE<<"new websocket request"; + LOG_TRACE << "new websocket request"; return true; } return false; } -void HttpServer::onRequest(const TcpConnectionPtr& conn, const HttpRequestPtr& req) +void HttpServer::onRequest(const TcpConnectionPtr &conn, const HttpRequestPtr &req) { - const std::string& connection = req->getHeader("Connection"); + const std::string &connection = req->getHeader("Connection"); bool _close = connection == "close" || (req->getVersion() == HttpRequestImpl::kHttp10 && connection != "Keep-Alive"); - bool _isHeadMethod=(req->method()==HttpRequest::kHead); - if(_isHeadMethod) + bool _isHeadMethod = (req->method() == HttpRequest::kHead); + if (_isHeadMethod) { req->setMethod(HttpRequest::kGet); } - HttpContext* context = any_cast(conn->getMutableContext()); + HttpContext *context = any_cast(conn->getMutableContext()); //request will be received in same thread,so we don't need mutex; context->pushRquestToPipeLine(req); - httpAsyncCallback_(req, [ = ](const HttpResponsePtr &response) { - - if(!response) + httpAsyncCallback_(req, [=](const HttpResponsePtr &response) { + if (!response) return; response->setCloseConnection(_close); //if the request method is HEAD,remove the body of response(rfc2616-9.4) - if(_isHeadMethod) + if (_isHeadMethod) response->setBody(std::string()); - auto & sendfileName=std::dynamic_pointer_cast(response)->sendfileName(); - - if(sendfileName.empty()&& - response->getContentTypeCode()getBody().length()>1024&& - req->getHeader("Accept-Encoding").find("gzip")!=std::string::npos&& - response->getHeader("Content-Encoding")=="") + auto &sendfileName = std::dynamic_pointer_cast(response)->sendfileName(); + auto newResp = response; + if (sendfileName.empty() && + response->getContentTypeCode() < CT_APPLICATION_OCTET_STREAM && + response->getBody().length() > 1024 && + req->getHeader("Accept-Encoding").find("gzip") != std::string::npos && + response->getHeader("Content-Encoding") == "") { //use gzip - LOG_TRACE<<"Use gzip to compress the body"; - char *zbuf=new char[response->getBody().length()]; - size_t zlen=response->getBody().length(); - if(gzipCompress(response->getBody().data(), - response->getBody().length(), - zbuf,&zlen)>=0) + LOG_TRACE << "Use gzip to compress the body"; + char *zbuf = new char[response->getBody().length()]; + size_t zlen = response->getBody().length(); + if (gzipCompress(response->getBody().data(), + response->getBody().length(), + zbuf, &zlen) >= 0) { - if(zlen>0) + if (zlen > 0) { - response->setBody(std::string(zbuf,zlen)); - response->addHeader("Content-Encoding","gzip"); + if (response->expiredTime() >= 0) + { + //cached response,we need to make a clone + newResp = std::make_shared(*std::dynamic_pointer_cast(response)); + } + newResp->setBody(std::string(zbuf, zlen)); + newResp->addHeader("Content-Encoding", "gzip"); } else { - LOG_ERROR<<"gzip got 0 length result"; + LOG_ERROR << "gzip got 0 length result"; } } - delete [] zbuf; + delete[] zbuf; } { /* @@ -214,42 +219,43 @@ void HttpServer::onRequest(const TcpConnectionPtr& conn, const HttpRequestPtr& r * rfc2616-8.1.1.2 */ std::lock_guard guard(context->getPipeLineMutex()); - if(context->getFirstRequest()==req) + if (context->getFirstRequest() == req) { context->popFirstRequest(); - sendResponse(conn,response); - while(1) + sendResponse(conn, newResp); + while (1) { - auto resp=context->getFirstResponse(); - if(resp) + auto resp = context->getFirstResponse(); + if (resp) { context->popFirstRequest(); - sendResponse(conn,resp); - } else + sendResponse(conn, resp); + } + else return; } } else { //some earlier requests are waiting for responses; - context->pushResponseToPipeLine(req,response); + context->pushResponseToPipeLine(req, newResp); } } - }); } -void HttpServer::sendResponse(const TcpConnectionPtr& conn, +void HttpServer::sendResponse(const TcpConnectionPtr &conn, const HttpResponsePtr &response) { MsgBuffer buf; std::dynamic_pointer_cast(response)->appendToBuffer(&buf); conn->send(std::move(buf)); - auto & sendfileName=std::dynamic_pointer_cast(response)->sendfileName(); - if(!sendfileName.empty()) + auto &sendfileName = std::dynamic_pointer_cast(response)->sendfileName(); + if (!sendfileName.empty()) { conn->sendFile(sendfileName.c_str()); } - if (response->closeConnection()) { + if (response->closeConnection()) + { conn->shutdown(); } }