This commit is contained in:
an-tao 2018-10-02 19:45:31 +08:00
commit a4b8d1d421
19 changed files with 273 additions and 111 deletions

View File

@ -7,21 +7,16 @@ IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
if(version LESS 4.9.0)
MESSAGE(STATUS "gcc is too old")
stop()
endif()
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17)
if(COMPILER_SUPPORTS_CXX17)
message(STATUS "support c++17")
elseif (version LESS 6.1.0)
MESSAGE(STATUS "c++11")
set(CMAKE_CXX_STD_FLAGS c++11)
elseif(version LESS 7.1.0)
set(CMAKE_CXX_STD_FLAGS c++14)
MESSAGE(STATUS "c++14")
else()
set(CMAKE_CXX_STD_FLAGS c++17)
set(DR_DEFS "USE_STD_ANY")
elseif(COMPILER_SUPPORTS_CXX14)
message(STATUS "support c++14")
set(CMAKE_CXX_STD_FLAGS c++14)
elseif(COMPILER_SUPPORTS_CXX11)
message(STATUS "support c++11")
set(CMAKE_CXX_STD_FLAGS c++11)
MESSAGE(STATUS "c++17")
endif()
else()
set(CMAKE_CXX_STD_FLAGS c++11)

View File

@ -1,6 +1,8 @@
![](https://github.com/an-tao/drogon/wiki/images/drogon-white.jpg)
**Drogon** is a C++11-based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. Drogon's main application platform is Linux. For debugging purposes, it also supports Mac OS. There is no plan to support Windows. Its main features are as follows:
**Drogon** is a C++11-based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. **Drogon** is the name of a dragon in the American TV series "Game of Thrones" that I really like.
Drogon's main application platform is Linux. For debugging purposes, it also supports Mac OS. There is no plan to support Windows. Its main features are as follows:
* The network layer uses a NIO framework based on epoll (poll under MacOS) to provide high-concurrency, high-performance network IO;
* Full asynchronous programming mode;
@ -11,7 +13,7 @@
* Support view page dynamic loading (dynamic compilation and loading at runtime);
* Very convenient and flexible path to controller handler mapping scheme;
* Support filter chain to facilitate the execution of unified logic (such as login verification, Http Method constraint verification, etc.) before the controller;
* Support https (based on OpenSSL implementation);
* Support https (based on OpenSSL);
* Support websocket (server side);
* Support Json format request and response;
* Support file download and upload;

View File

@ -41,6 +41,8 @@
"file_types":["gif","png","jpg","js","css","html","ico","swf","xap","apk","cur","xml"],
//max_connections:max connections number,100000 by default
"max_connections":100000,
//max_connections_per_ip:max connections number per clinet,0 by default which means no limit
"max_connections_per_ip":0,
//Load_dynamic_views: false by default, when set to true, drogon will
//compile and load dynamically "CSP View Files" in directories defined
//by "dynamic_views_path"
@ -56,12 +58,15 @@
//drogon.log ...
"logfile_base_name":"",
//log_size_limit:100000000 bytes by default,
//When the log file size reaches "log_size_limit", the log file is switched.
//When the log file size reaches "log_size_limit", the log file will be switched.
"log_size_limit":100000000
},
//run_as_daemon:false by default
"run_as_daemon":false,
//relaunch_on_error:false by default,if true,the program will be restart by parent after exit;
"relaunch_on_error":false
"relaunch_on_error":false,
//use_sendfile:true by default,if ture,the program will
//use sendfile() system-call to send static file to client;
"use_sendfile":true
}
}
}

View File

@ -28,31 +28,28 @@ static void newCmakeFile(std::ofstream &cmakeFile,const std::string &projectName
"if(OpenSSL_FOUND)\n"
"link_libraries(ssl crypto)\n"
"endif()\n"
"\n"
"IF (CMAKE_SYSTEM_NAME MATCHES \"Linux\")\n"
" EXEC_PROGRAM (gcc ARGS \"--version | grep '^gcc'|awk '{print $3}' | sed s'/)//g' | sed s'/-.*//g'\" OUTPUT_VARIABLE version)\n"
" MESSAGE(STATUS \"This is gcc version:: \" ${version})\n"
" if(version LESS 4.9.0)\n"
" MESSAGE(STATUS \"gcc is too old\")\n"
" stop()\n"
" endif()\n"
" include(CheckCXXCompilerFlag)\n"
" CHECK_CXX_COMPILER_FLAG(\"-std=c++11\" COMPILER_SUPPORTS_CXX11)\n"
" CHECK_CXX_COMPILER_FLAG(\"-std=c++14\" COMPILER_SUPPORTS_CXX14)\n"
" CHECK_CXX_COMPILER_FLAG(\"-std=c++17\" COMPILER_SUPPORTS_CXX17)\n"
" if(COMPILER_SUPPORTS_CXX17)\n"
" message(STATUS \"support c++17\")\n"
" set(CMAKE_CXX_STD_FLAGS c++17)\n"
" set(DEFS \"USE_STD_ANY\")\n"
" elseif(COMPILER_SUPPORTS_CXX14)\n"
" message(STATUS \"support c++14\")\n"
" set(CMAKE_CXX_STD_FLAGS c++14)\n"
" elseif(COMPILER_SUPPORTS_CXX11)\n"
" message(STATUS \"support c++11\")\n"
" elseif (version LESS 6.1.0)\n"
" MESSAGE(STATUS \"c++11\")\n"
" set(CMAKE_CXX_STD_FLAGS c++11)\n"
" elseif(version LESS 7.1.0)\n"
" set(CMAKE_CXX_STD_FLAGS c++14)\n"
" MESSAGE(STATUS \"c++14\")\n"
" else()\n"
" set(CMAKE_CXX_STD_FLAGS c++17)\n"
" set(DR_DEFS \"USE_STD_ANY\")\n"
" MESSAGE(STATUS \"c++17\")\n"
" endif()\n"
"else()\n"
" set(CMAKE_CXX_STD_FLAGS c++11)\n"
"endif()\n"
"\n"
"if(CMAKE_BUILD_TYPE STREQUAL \"\")\n"
" set(CMAKE_BUILD_TYPE Release)\n"
"endif()\n"
@ -88,9 +85,9 @@ static void newMainFile(std::ofstream &mainFile)
{
mainFile<<"#include <drogon/HttpAppFramework.h>\n"
"int main() {\n"
" //设置http监听的地址和端口\n"
" //Set HTTP listener address and port\n"
" drogon::HttpAppFramework::instance().addListener(\"0.0.0.0\",80);\n"
" //运行http框架程序阻塞在底层的事件循环中\n"
" //Run HTTP framework,the method will block in the inner event loop\n"
" drogon::HttpAppFramework::instance().run();\n"
" return 0;\n"
"}";
@ -200,6 +197,80 @@ static void newJsonFindFile(std::ofstream &jsonFile)
"\tJSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)\n"
"mark_as_advanced (JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)\n";
}
static void newConfigFile(std::ofstream &configFile)
{
configFile<<"/* This is a JSON format configuration file\n"
"*/\n"
"{\n"
" //ssl:the global ssl files setting\n"
" \"ssl\": {\n"
" \"cert\": \"../../trantor/trantor/tests/server.pem\",\n"
" \"key\": \"../../trantor/trantor/tests/server.pem\"\n"
" },\n"
" \"listeners\": [\n"
" {\n"
" //address:ip address,0.0.0.0 by default\n"
" \"address\": \"0.0.0.0\",\n"
" //port:port number\n"
" \"port\": 80,\n"
" //https:if use https for security,false by default\n"
" \"https\": false\n"
" },\n"
" {\n"
" \"address\": \"0.0.0.0\",\n"
" \"port\": 443,\n"
" \"https\": true,\n"
" //cert,key:cert file path and key file path,empty by default,\n"
" //if empty,use global setting\n"
" \"cert\": \"\",\n"
" \"key\": \"\"\n"
" }\n"
" ],\n"
" \"app\": {\n"
" //threads_num:num of threads,1 by default\n"
" \"threads_num\": 16,\n"
" //enable_session:false by default\n"
" \"enable_session\": false,\n"
" \"session_timeout\": 0,\n"
" //document_root:Root path of HTTP document,defaut path is ./\n"
" \"document_root\":\"./\",\n"
" /* file_types:\n"
" * HTTP download file types,The file types supported by drogon\n"
" * by default are \"html\", \"js\", \"css\", \"xml\", \"xsl\", \"txt\", \"svg\",\n"
" * \"ttf\", \"otf\", \"woff2\", \"woff\" , \"eot\", \"png\", \"jpg\", \"jpeg\",\n"
" * \"gif\", \"bmp\", \"ico\", \"icns\", etc. */\n"
" \"file_types\":[\"gif\",\"png\",\"jpg\",\"js\",\"css\",\"html\",\"ico\",\"swf\",\"xap\",\"apk\",\"cur\",\"xml\"],\n"
" //max_connections:max connections number,100000 by default\n"
" \"max_connections\":100000,\n"
" //Load_dynamic_views: false by default, when set to true, drogon will\n"
" //compile and load dynamically \"CSP View Files\" in directories defined\n"
" //by \"dynamic_views_path\"\n"
" \"load_dynamic_views\":true,\n"
" //dynamic_views_path: if the path isn't prefixed with / or ./,\n"
" //it will be relative path of document_root path\n"
" \"dynamic_views_path\":[\"./views\"],\n"
" //log:set log output,drogon output logs to stdout by default\n"
" \"log\":{\n"
" //log_path:log file path,empty by default,in which case,log will output to the stdout\n"
" \"log_path\":\"./\",\n"
" //logfile_base_name:log file base name,empty by default which means drogon will name logfile as\n"
" //drogon.log ...\n"
" \"logfile_base_name\":\"\",\n"
" //log_size_limit:100000000 bytes by default,\n"
" //When the log file size reaches \"log_size_limit\", the log file is switched.\n"
" \"log_size_limit\":100000000\n"
" },\n"
" //run_as_daemon:false by default\n"
" \"run_as_daemon\":false,\n"
" //relaunch_on_error:false by default,if true,the program will be restart by parent after exit;\n"
" \"relaunch_on_error\":false,\n"
" //use_sendfile:true by default,if ture,the program will\n"
" //use sendfile() system-call to send static file to client;\n"
" \"use_sendfile\":true\n"
" }\n"
"}\n";
}
void create_project::createProject(const std::string &projectName)
{
@ -211,7 +282,8 @@ void create_project::createProject(const std::string &projectName)
std::cout<<"create a project named "<<projectName<<std::endl;
mkdir(projectName.data(),0755);
//1.create CMakeLists.txt
chdir(projectName.data());
auto r=chdir(projectName.data());
(void)(r);
std::ofstream cmakeFile("CMakeLists.txt",std::ofstream::out);
newCmakeFile(cmakeFile,projectName);
std::ofstream mainFile("main.cc",std::ofstream::out);
@ -225,4 +297,6 @@ void create_project::createProject(const std::string &projectName)
newJsonFindFile(jsonFile);
std::ofstream gitFile(".gitignore",std::ofstream::out);
newGitIgFile(gitFile);
std::ofstream configFile("config.json",std::ofstream::out);
newConfigFile(configFile);
}

View File

@ -9,7 +9,13 @@ void TimeFilter::doFilter(const HttpRequestPtr& req,
const FilterChainCallback &ccb)
{
trantor::Date now=trantor::Date::date();
LOG_TRACE<<"";
if(!req->session())
{
//no session support by framework,pls enable session
auto resp=HttpResponse::newNotFoundResponse();
cb(resp);
return;
}
if(req->session()->find(VDate))
{
auto lastDate=req->session()->get<trantor::Date>(VDate);

View File

@ -10,8 +10,8 @@ namespace api
public:
METHOD_LIST_BEGIN
//use METHOD_ADD to add your custom processing function here;
METHOD_ADD(ApiTest::get,"/get/{2}/{1}","drogon::GetFilter");//path will be /api/v1/ApiTest/get/{arg2}/{arg1}
METHOD_ADD(ApiTest::your_method_name,"/{1}/list?p2={2}","drogon::GetFilter");//path will be /api/v1/ApiTest/{arg1}/list
METHOD_ADD(ApiTest::get,"/get/{2}/{1}","drogon::GetFilter");//path will be /api/v1/apitest/get/{arg2}/{arg1}
METHOD_ADD(ApiTest::your_method_name,"/{1}/list?p2={2}","drogon::GetFilter");//path will be /api/v1/apitest/{arg1}/list
METHOD_LIST_END
//your declaration of processing function maybe like this:

View File

@ -21,6 +21,22 @@ public:
para["string p3"]=p3;
para["int p4"]=std::to_string(p4);
data.insert("parameters",para);
auto res=HttpResponse::newHttpViewResponse("ListParaView",data);
callback(res);
}
static void staticHandle(const HttpRequestPtr& req,
const std::function<void (const HttpResponsePtr &)>&callback,
int p1,const std::string &p2,const std::string &p3,int p4)
{
HttpViewData data;
data.insert("title",std::string("ApiTest::get"));
std::map<std::string,std::string> 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);
callback(res);
@ -83,7 +99,7 @@ int main()
std::cout<<banner<<std::endl;
drogon::HttpAppFramework::instance().addListener("0.0.0.0",12345);
// drogon::HttpAppFramework::instance().addListener("0.0.0.0",12345);
drogon::HttpAppFramework::instance().addListener("0.0.0.0",8080);
//#ifdef USE_OPENSSL
// //https
@ -95,6 +111,7 @@ int main()
trantor::Logger::setLogLevel(trantor::Logger::TRACE);
//class function
drogon::HttpAppFramework::registerHttpApiMethod("/api/v1/handle1/{1}/{2}/?p3={3}&p4={4}",&A::handle);
drogon::HttpAppFramework::registerHttpApiMethod("/api/v1/handle11/{1}/{2}/?p3={3}&p4={4}",&A::staticHandle);
//lambda example
drogon::HttpAppFramework::registerHttpApiMethod("/api/v1/handle2/{1}/{2}",[](const HttpRequestPtr&req,const std::function<void (const HttpResponsePtr &)>&callback,int a,float b){
LOG_DEBUG<<"int a="<<a;
@ -123,7 +140,7 @@ int main()
drogon::HttpAppFramework::instance().enableSession(1200);
//start app framework
//drogon::HttpAppFramework::instance().enableDynamicViewsLoading({"/tmp/views"});
drogon::HttpAppFramework::instance().loadConfigFile("../../drogon.conf.example");
drogon::HttpAppFramework::instance().loadConfigFile("../../config.json.example");
drogon::HttpAppFramework::instance().run();
}

View File

@ -40,6 +40,7 @@ namespace drogon
__cxxabiv1::__cxa_demangle( mangled_name, nullptr, &len, &status ), &std::free ) ;
return ptr.get() ;
}
virtual ~DrObjectBase(){}
};

View File

@ -75,9 +75,6 @@ namespace drogon
virtual void registerHttpSimpleController(const std::string &pathName,
const std::string &crtlName,
const std::vector<std::string> &filters=std::vector<std::string>())=0;
virtual void registerHttpApiController(const std::string &pathPattern,
const HttpApiBinderBasePtr &binder,
const std::vector<std::string> &filters=std::vector<std::string>())=0;
template <typename FUNCTION>
static void registerHttpApiMethod(const std::string &pathPattern,
FUNCTION && function,
@ -100,6 +97,7 @@ namespace drogon
virtual void enableDynamicViewsLoading(const std::vector<std::string> &libPaths)=0;
virtual void setMaxConnectionNum(size_t maxConnections)=0;
virtual void setMaxConnectionNumPerIP(size_t maxConnectionsPerIP)=0;
virtual void loadConfigFile(const std::string &fileName)=0;
@ -109,5 +107,12 @@ namespace drogon
virtual void setLogPath(const std::string &logPath,
const std::string &logfileBaseName="",
size_t logSize=100000000)=0;
virtual void enableSendfile(bool sendFile)=0;
private:
virtual void registerHttpApiController(const std::string &pathPattern,
const HttpApiBinderBasePtr &binder,
const std::vector<std::string> &filters=std::vector<std::string>())=0;
};
}

View File

@ -161,6 +161,8 @@ namespace drogon
static HttpResponsePtr newHttpJsonResponse(const Json::Value &data);
static HttpResponsePtr newHttpViewResponse(const std::string &viewName,const HttpViewData& data);
static HttpResponsePtr newLocationRedirectResponse(const std::string &path);
virtual ~HttpResponse(){}
};
}

View File

@ -24,6 +24,7 @@ namespace drogon{
template<typename> struct FunctionTraits;
//functor,lambda,std::function...
template <typename Function>
struct FunctionTraits : public FunctionTraits<
decltype(&std::remove_reference<Function>::type::operator())
@ -33,7 +34,7 @@ namespace drogon{
{return std::string("Functor");}
};
//class instance method of const object
template <
typename ClassType,
typename ReturnType,
@ -47,8 +48,7 @@ namespace drogon{
static const std::string name(){return std::string("Class Function");}
};
/* support the non-const operator ()
* this will work with user defined functors */
//class instance method of non-const object
template <
typename ClassType,
typename ReturnType,
@ -62,31 +62,7 @@ namespace drogon{
static const std::string name(){return std::string("Class Function");}
};
//class function
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct FunctionTraits<
ReturnType(ClassType::*)(const HttpRequestPtr& req,const std::function<void (const HttpResponsePtr &)>&callback,Arguments...) const
> : FunctionTraits<ReturnType(ClassType::*)(Arguments...)> {
static const bool isHTTPApiFunction=true;
static const std::string name(){return std::string("Class Const Api Function");}
};
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct FunctionTraits<
ReturnType(ClassType::*)(const HttpRequestPtr& req,const std::function<void (const HttpResponsePtr &)>&callback,Arguments...)
> : FunctionTraits<ReturnType(ClassType::*)(Arguments...)> {
static const bool isHTTPApiFunction=true;
static const std::string name(){return std::string("Class Api Function");}
};
//normal function
//normal function for HTTP handling
template <
typename ReturnType,
typename... Arguments
@ -97,18 +73,8 @@ namespace drogon{
static const bool isHTTPApiFunction=true;
};
//std::function
// template <
// typename ReturnType,
// typename... Arguments
// >
// struct FunctionTraits<std::function<
// ReturnType(const HttpRequest& req,const std::function<void (const HttpResponsePtr &)>& callback,Arguments...)>>
// :FunctionTraits<ReturnType(*)(Arguments...)> {
// static const bool isHTTPApiFunction=true;
// static const std::string name(){return std::string("std::function");}
// };
//normal function
template <
typename ReturnType,
typename... Arguments

View File

@ -77,6 +77,8 @@ static void loadApp(const Json::Value &app)
auto timeout=app.get("session_timeout",0).asUInt64();
if(enableSession)
HttpAppFramework::instance().enableSession(timeout);
else
HttpAppFramework::instance().disableSession();
//document root
auto documentRoot=app.get("document_root","").asString();
if(documentRoot!="")
@ -101,6 +103,13 @@ static void loadApp(const Json::Value &app)
{
HttpAppFramework::instance().setMaxConnectionNum(maxConns);
}
//max connections per IP
auto maxConnsPerIP=app.get("max_connections_per_ip",0).asUInt64();
if(maxConnsPerIP>0)
{
HttpAppFramework::instance().setMaxConnectionNumPerIP(maxConnsPerIP);
}
//dynamic views
auto enableDynamicViews=app.get("load_dynamic_views",false).asBool();
if(enableDynamicViews)
@ -131,6 +140,8 @@ static void loadApp(const Json::Value &app)
{
HttpAppFramework::instance().enableRelaunchOnError();
}
auto useSendfile=app.get("use_sendfile",true).asBool();
HttpAppFramework::instance().enableSendfile(useSendfile);
}
static void loadListeners(const Json::Value &listeners)
{
@ -157,7 +168,7 @@ static void loadSSL(const Json::Value &sslFiles)
HttpAppFramework::instance().setSSLFiles(cert,key);
}
void ConfigLoader::load() {
std::cout<<_configJsonRoot<<std::endl;
//std::cout<<_configJsonRoot<<std::endl;
loadApp(_configJsonRoot["app"]);
loadSSL(_configJsonRoot["ssl"]);
loadListeners(_configJsonRoot["listeners"]);

View File

@ -229,6 +229,10 @@ void HttpAppFrameworkImpl::setMaxConnectionNum(size_t maxConnections)
{
_maxConnectionNum=maxConnections;
}
void HttpAppFrameworkImpl::setMaxConnectionNumPerIP(size_t maxConnectionsPerIP)
{
_maxConnectionNumPerIP=maxConnectionsPerIP;
}
void HttpAppFrameworkImpl::loadConfigFile(const std::string &fileName)
{
ConfigLoader loader(fileName);
@ -506,10 +510,30 @@ void HttpAppFrameworkImpl::onConnection(const TcpConnectionPtr &conn)
LOG_ERROR<<"too much connections!force close!";
conn->forceClose();
}
else if(_maxConnectionNumPerIP>0)
{
{
auto iter=_connectionsNumMap.find(conn->peerAddr().toIp());
if(iter==_connectionsNumMap.end())
{
_connectionsNumMap[conn->peerAddr().toIp()]=0;
}
if(_connectionsNumMap[conn->peerAddr().toIp()]++>=_maxConnectionNumPerIP)
{
conn->forceClose();
}
}
}
}
else
{
_connectionNum--;
if(_maxConnectionNumPerIP>0&&_connectionsNumMap.find(conn->peerAddr().toIp())!=_connectionsNumMap.end())
{
std::lock_guard<std::mutex> guard(_connectionsNumMapMutex);
_connectionsNumMap[conn->peerAddr().toIp()]--;
}
}
}
std::string parseWebsockFrame(trantor::MsgBuffer *buffer)
@ -892,18 +916,26 @@ void HttpAppFrameworkImpl::readSendFile(const std::string& filePath,const HttpRe
}
}
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);
infile.close();
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<HttpResponseImpl>(resp)->setSendfile(filePath);
}
else
{
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);
LOG_INFO << "file len:" << str.length();
resp->setBody(std::move(str));
}
resp->setStatusCode(HttpResponse::k200OK);
LOG_INFO << "file len:" << str.length();
resp->setBody(std::move(str));
}
trantor::EventLoop *HttpAppFrameworkImpl::loop()

View File

@ -33,7 +33,7 @@ namespace drogon
{
public:
HttpAppFrameworkImpl():
_connectionNum(0)
_connectionNum(0)
{
}
virtual void addListener(const std::string &ip,
@ -53,9 +53,6 @@ namespace drogon
const std::string &crtlName,
const std::vector<std::string> &filters=
std::vector<std::string>())override ;
virtual void registerHttpApiController(const std::string &pathPattern,
const HttpApiBinderBasePtr &binder,
const std::vector<std::string> &filters=std::vector<std::string>()) override ;
virtual void enableSession(const size_t timeout=0) override { _useSession=true;_sessionTimeout=timeout;}
virtual void disableSession() override { _useSession=false;}
virtual const std::string & getDocumentRoot() const override {return _rootPath;}
@ -63,20 +60,26 @@ namespace drogon
virtual void setFileTypes(const std::vector<std::string> &types) override;
virtual void enableDynamicViewsLoading(const std::vector<std::string> &libPaths) override;
virtual void setMaxConnectionNum(size_t maxConnections) override;
virtual void setMaxConnectionNumPerIP(size_t maxConnectionsPerIP) override;
virtual void loadConfigFile(const std::string &fileName) override;
virtual void enableRunAsDaemon() override {_runAsDaemon=true;}
virtual void enableRelaunchOnError() override {_relaunchOnError=true;}
virtual void setLogPath(const std::string &logPath,
const std::string &logfileBaseName="",
size_t logfileSize=100000000) override;
virtual void enableSendfile(bool sendFile) override {_useSendfile=sendFile;}
~HttpAppFrameworkImpl(){
//Destroy the following objects before _loop destruction
_sharedLibManagerPtr.reset();
_sessionMapPtr.reset();
}
//Destroy the following objects before _loop destruction
_sharedLibManagerPtr.reset();
_sessionMapPtr.reset();
}
trantor::EventLoop *loop();
private:
virtual void registerHttpApiController(const std::string &pathPattern,
const HttpApiBinderBasePtr &binder,
const std::vector<std::string> &filters=std::vector<std::string>()) override ;
std::vector<std::tuple<std::string,uint16_t,bool,std::string,std::string>> _listeners;
void onAsyncRequest(const HttpRequestPtr& req,const std::function<void (const HttpResponsePtr &)> & callback);
void onNewWebsockRequest(const HttpRequestPtr& req,
@ -104,11 +107,11 @@ namespace drogon
const std::string &session_id,
const std::function<void ()> &missCallback);
void doFilterChain(const std::shared_ptr<std::queue<std::shared_ptr<HttpFilterBase>>> &chain,
const HttpRequestPtr& req,
const std::function<void (const HttpResponsePtr &)> & callback,
bool needSetJsessionid,
const std::string &session_id,
const std::function<void ()> &missCallback);
const HttpRequestPtr& req,
const std::function<void (const HttpResponsePtr &)> & callback,
bool needSetJsessionid,
const std::string &session_id,
const std::function<void ()> &missCallback);
//
struct ControllerAndFiltersName
{
@ -162,12 +165,19 @@ namespace drogon
std::string _sslKeyPath;
size_t _maxConnectionNum=100000;
size_t _maxConnectionNumPerIP=0;
std::atomic<uint64_t> _connectionNum;
std::unordered_map<std::string,size_t> _connectionsNumMap;
std::mutex _connectionsNumMapMutex;
bool _runAsDaemon=false;
bool _relaunchOnError=false;
std::string _logPath="";
std::string _logfileBaseName="";
size_t _logfileSize=100000000;
bool _useSendfile=true;
};
}

View File

@ -27,6 +27,7 @@
#include <drogon/HttpAppFramework.h>
#include <trantor/utils/Logger.h>
#include <stdio.h>
#include <sys/stat.h>
using namespace trantor;
using namespace drogon;
@ -313,12 +314,30 @@ std::string HttpResponseImpl::web_response_code_to_string(int code)
}
void HttpResponseImpl::appendToBuffer(MsgBuffer* output) const
{
char buf[32];
char buf[64];
snprintf(buf, sizeof buf, "HTTP/1.1 %d ", _statusCode);
output->append(buf);
output->append(_statusMessage);
output->append("\r\n");
snprintf(buf, sizeof buf, "Content-Length: %lu\r\n", _body.size());
if(_sendfileName.empty())
{
snprintf(buf, sizeof buf, "Content-Length: %lu\r\n", _body.size());
}
else
{
struct stat filestat;
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);
#else
snprintf(buf, sizeof buf, "Content-Length: %lld\r\n",filestat.st_size);
#endif
}
output->append(buf);
if(_headers.find("Connection")==_headers.end())
{

View File

@ -268,6 +268,12 @@ namespace drogon
}
return _jsonPtr;
}
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,
@ -288,6 +294,7 @@ namespace drogon
size_t _current_chunk_length;
uint8_t _contentType=CT_TEXT_HTML;
std::string _sendfileName;
mutable std::shared_ptr<Json::Value> _jsonPtr;
//trantor::Date receiveTime_;

View File

@ -177,7 +177,10 @@ void HttpServer::onRequest(const TcpConnectionPtr& conn, const HttpRequestPtr& r
//if the request method is HEAD,remove the body of response(rfc2616-9.4)
if(_isHeadMethod)
response->setBody(std::string());
if(response->getContentTypeCode()<CT_APPLICATION_OCTET_STREAM&&
auto & sendfileName=std::dynamic_pointer_cast<HttpResponseImpl>(response)->sendfileName();
if(sendfileName.empty()&&
response->getContentTypeCode()<CT_APPLICATION_OCTET_STREAM&&
response->getBody().length()>1024&&
req->getHeader("Accept-Encoding").find("gzip")!=std::string::npos)
{
@ -240,6 +243,11 @@ void HttpServer::sendResponse(const TcpConnectionPtr& conn,
MsgBuffer buf;
std::dynamic_pointer_cast<HttpResponseImpl>(response)->appendToBuffer(&buf);
conn->send(std::move(buf));
auto & sendfileName=std::dynamic_pointer_cast<HttpResponseImpl>(response)->sendfileName();
if(!sendfileName.empty())
{
conn->sendFile(sendfileName.c_str());
}
if (response->closeConnection()) {
conn->shutdown();
}

View File

@ -115,7 +115,9 @@ void SharedLibManager::managerLibs()
std::string cmd="drogon_ctl create view ";
cmd.append(filename).append(" -o ").append(libPath);
LOG_TRACE<<cmd;
system(cmd.c_str());
auto r=system(cmd.c_str());
//FIXME:handle r
(void)(r);
auto srcFile=filename.substr(0,pos);
srcFile.append(".cc");
DLStat dlStat;

@ -1 +1 @@
Subproject commit 4c0a8bb59b4c77622fb004df932401422a966492
Subproject commit 5555d02c1a33b8597d35ca56a614d9ecafdc41ca