From 91817216799b6c629abc876c434419eadf92346f Mon Sep 17 00:00:00 2001 From: antao Date: Mon, 15 Oct 2018 11:13:14 +0800 Subject: [PATCH 1/8] Cache the static files response --- lib/src/HttpAppFrameworkImpl.cc | 51 ++++++++++++++++++++++++++++----- lib/src/HttpAppFrameworkImpl.h | 6 +++- lib/src/HttpContext.cc | 8 +++--- lib/src/HttpResponseImpl.cc | 4 +-- lib/src/HttpResponseImpl.h | 21 ++++++++------ 5 files changed, 67 insertions(+), 23 deletions(-) diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 0c3c9335..33e06b35 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -709,6 +709,30 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: { LOG_INFO << "file query!"; std::string filePath = _rootPath + path; + //find cached response + HttpResponsePtr cachedResp; + if (_staticFilesCache.find(filePath) != _staticFilesCache.end()) + { + std::lock_guard guard(_staticFilesCacheMutex); + cachedResp = _staticFilesCache[filePath].lock(); + if (!cachedResp) + { + _staticFilesCache.erase(filePath); + } + } + if (cachedResp) + { + if (needSetJsessionid) + { + //make a copy + auto newCachedResp = std::make_shared(*std::dynamic_pointer_cast(cachedResp)); + newCachedResp->addCookie("JSESSIONID", session_id); + callback(newCachedResp); + return; + } + callback(cachedResp); + return; + } std::shared_ptr resp = std::make_shared(); if (needSetJsessionid) @@ -969,7 +993,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: } } -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 std::ifstream infile(filePath, std::ifstream::binary); @@ -1010,26 +1034,39 @@ void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpR } } + std::streambuf *pbuf = infile.rdbuf(); + std::streamsize filesize = pbuf->pubseekoff(0, infile.end); + pbuf->pubseekoff(0, infile.beg); // rewind + if (_useSendfile && (req->getHeader("Accept-Encoding").find("gzip") == std::string::npos || - resp->getContentTypeCode() >= CT_APPLICATION_OCTET_STREAM)) + resp->getContentTypeCode() >= CT_APPLICATION_OCTET_STREAM) && + filesize > 1024 * 100) { //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::string str; - str.resize(size); - pbuf->sgetn(&str[0], size); + str.resize(filesize); + pbuf->sgetn(&str[0], filesize); LOG_INFO << "file len:" << str.length(); resp->setBody(std::move(str)); } resp->setStatusCode(HttpResponse::k200OK); + + //cache the response for 5 seconds + resp->setExpiredTime(5); + _responseCacheMap->insert(filePath, resp, resp->expiredTime(), [=]() { + std::lock_guard guard(_staticFilesCacheMutex); + _staticFilesCache.erase(filePath); + }); + { + std::lock_guard guard(_staticFilesCacheMutex); + _staticFilesCache[filePath] = resp; + } } trantor::EventLoop *HttpAppFrameworkImpl::loop() diff --git a/lib/src/HttpAppFrameworkImpl.h b/lib/src/HttpAppFrameworkImpl.h index 9fe5b7e4..e9117e94 100644 --- a/lib/src/HttpAppFrameworkImpl.h +++ b/lib/src/HttpAppFrameworkImpl.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace drogon @@ -95,7 +96,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework void onWebsockMessage(const WebSocketConnectionPtr &wsConnPtr, trantor::MsgBuffer *buffer); void onWebsockDisconnect(const WebSocketConnectionPtr &wsConnPtr); void onConnection(const TcpConnectionPtr &conn); - void readSendFile(const std::string &filePath, const HttpRequestPtr &req, const HttpResponsePtr resp); + void readSendFile(const std::string &filePath, const HttpRequestPtr &req, const HttpResponsePtr &resp); void addApiPath(const std::string &path, const HttpApiBinderBasePtr &binder, const std::vector &filters); @@ -190,5 +191,8 @@ class HttpAppFrameworkImpl : public HttpAppFramework size_t _logfileSize = 100000000; bool _useSendfile = true; bool _useGzip = true; + + std::unordered_map> _staticFilesCache; + std::mutex _staticFilesCacheMutex; }; } // namespace drogon diff --git a/lib/src/HttpContext.cc b/lib/src/HttpContext.cc index edf95897..fe0a68af 100755 --- a/lib/src/HttpContext.cc +++ b/lib/src/HttpContext.cc @@ -339,12 +339,12 @@ bool HttpContext::parseResponse(MsgBuffer *buf) if (response_._left_body_length >= buf->readableBytes()) { response_._left_body_length -= buf->readableBytes(); - response_._body += std::string(buf->peek(), buf->readableBytes()); + response_._bodyPtr->append(std::string(buf->peek(), buf->readableBytes())); buf->retrieveAll(); } else { - response_._body += std::string(buf->peek(), response_._left_body_length); + response_._bodyPtr->append(std::string(buf->peek(), response_._left_body_length)); buf->retrieve(request_->contentLen); response_._left_body_length = 0; } @@ -359,7 +359,7 @@ bool HttpContext::parseResponse(MsgBuffer *buf) } else if (res_state_ == HttpResponseParseState::kExpectClose) { - response_._body += std::string(buf->peek(), buf->readableBytes()); + response_._bodyPtr->append(std::string(buf->peek(), buf->readableBytes())); buf->retrieveAll(); break; } @@ -396,7 +396,7 @@ bool HttpContext::parseResponse(MsgBuffer *buf) if (*(buf->peek() + response_._current_chunk_length) == '\r' && *(buf->peek() + response_._current_chunk_length + 1) == '\n') { - response_._body += std::string(buf->peek(), response_._current_chunk_length); + response_._bodyPtr->append(std::string(buf->peek(), response_._current_chunk_length)); buf->retrieve(response_._current_chunk_length + 2); response_._current_chunk_length = 0; res_state_ = HttpResponseParseState::kExpectChunkLen; diff --git a/lib/src/HttpResponseImpl.cc b/lib/src/HttpResponseImpl.cc index 5d0d6835..805a6673 100755 --- a/lib/src/HttpResponseImpl.cc +++ b/lib/src/HttpResponseImpl.cc @@ -324,7 +324,7 @@ void HttpResponseImpl::makeHeaderString(MsgBuffer *output) const output->append("\r\n"); if (_sendfileName.empty()) { - snprintf(buf, sizeof buf, "Content-Length: %lu\r\n", _body.size()); + snprintf(buf, sizeof buf, "Content-Length: %lu\r\n", _bodyPtr->size()); } else { @@ -397,5 +397,5 @@ void HttpResponseImpl::appendToBuffer(MsgBuffer *output) const output->append("\r\n\r\n"); LOG_TRACE << "reponse(no body):" << output->peek(); - output->append(_body); + output->append(*_bodyPtr); } diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index a9eb70c6..4db18ef1 100755 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -47,7 +47,8 @@ class HttpResponseImpl : public HttpResponse : _statusCode(kUnknown), _closeConnection(false), _left_body_length(0), - _current_chunk_length(0) + _current_chunk_length(0), + _bodyPtr(new std::string()) { } virtual HttpStatusCode statusCode() override @@ -205,11 +206,11 @@ class HttpResponseImpl : public HttpResponse virtual void setBody(const std::string &body) override { - _body = body; + _bodyPtr = std::make_shared(body); } virtual void setBody(std::string &&body) override { - _body = std::move(body); + _bodyPtr = std::make_shared(std::move(body)); } virtual void redirect(const std::string &url) override @@ -226,7 +227,7 @@ class HttpResponseImpl : public HttpResponse _fullHeaderString.reset(); _headers.clear(); _cookies.clear(); - _body.clear(); + _bodyPtr.reset(new std::string()); _left_body_length = 0; _current_chunk_length = 0; } @@ -245,11 +246,11 @@ class HttpResponseImpl : public HttpResponse virtual const std::string &getBody() const override { - return _body; + return *_bodyPtr; } virtual std::string &getBody() override { - return _body; + return *_bodyPtr; } void swap(HttpResponseImpl &that) { @@ -259,7 +260,7 @@ class HttpResponseImpl : public HttpResponse std::swap(_v, that._v); _statusMessage.swap(that._statusMessage); std::swap(_closeConnection, that._closeConnection); - _body.swap(that._body); + _bodyPtr.swap(that._bodyPtr); std::swap(_left_body_length, that._left_body_length); std::swap(_current_chunk_length, that._current_chunk_length); std::swap(_contentType, that._contentType); @@ -274,7 +275,7 @@ class HttpResponseImpl : public HttpResponse 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 (!reader->parse(_bodyPtr->data(), _bodyPtr->data() + _bodyPtr->size(), _jsonPtr.get(), &errs)) { LOG_ERROR << errs; _jsonPtr.reset(); @@ -320,9 +321,11 @@ class HttpResponseImpl : public HttpResponse Version _v; std::string _statusMessage; bool _closeConnection; - std::string _body; + size_t _left_body_length; size_t _current_chunk_length; + std::shared_ptr _bodyPtr; + uint8_t _contentType = CT_TEXT_HTML; ssize_t _expriedTime = -1; From af8a1ef15a7b12b6de1507333ddaafb242368354 Mon Sep 17 00:00:00 2001 From: antao Date: Mon, 15 Oct 2018 11:31:30 +0800 Subject: [PATCH 2/8] Adjust critical area protected by _staticFilesCacheMutex --- lib/src/HttpAppFrameworkImpl.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 33e06b35..3e0eb6c2 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -711,13 +711,15 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: std::string filePath = _rootPath + path; //find cached response HttpResponsePtr cachedResp; - if (_staticFilesCache.find(filePath) != _staticFilesCache.end()) { std::lock_guard guard(_staticFilesCacheMutex); - cachedResp = _staticFilesCache[filePath].lock(); - if (!cachedResp) + if (_staticFilesCache.find(filePath) != _staticFilesCache.end()) { - _staticFilesCache.erase(filePath); + cachedResp = _staticFilesCache[filePath].lock(); + if (!cachedResp) + { + _staticFilesCache.erase(filePath); + } } } if (cachedResp) From b539b5231c4f05ce34698ad7e9f9f7b39458694f Mon Sep 17 00:00:00 2001 From: antao Date: Mon, 15 Oct 2018 13:34:38 +0800 Subject: [PATCH 3/8] Modify drogon_ctl --- config.example.json | 5 +- drogon_ctl/CMakeLists.txt | 23 +++++++- drogon_ctl/create_project.cc | 78 +------------------------ drogon_ctl/templates/config.csp | 94 +++++++++++++++++++++++++++++++ lib/inc/drogon/HttpAppFramework.h | 2 + lib/src/ConfigLoader.cc | 2 + lib/src/HttpAppFrameworkImpl.cc | 4 +- lib/src/HttpAppFrameworkImpl.h | 4 +- 8 files changed, 130 insertions(+), 82 deletions(-) create mode 100644 drogon_ctl/templates/config.csp diff --git a/config.example.json b/config.example.json index 1514148d..af61e473 100644 --- a/config.example.json +++ b/config.example.json @@ -86,6 +86,9 @@ //use sendfile() system-call to send static file to client; "use_sendfile": true, //use_gzip:true by default,use gzip to compress the response body's content; - "use_gzip": true + "use_gzip": true, + //static_files_cache_time:5 by default,the time in which static file response is cached, + //0 means cache forever,the negative value means no cache + "static_files_cache_time":5 } } diff --git a/drogon_ctl/CMakeLists.txt b/drogon_ctl/CMakeLists.txt index 23eace7a..94bcb8a7 100755 --- a/drogon_ctl/CMakeLists.txt +++ b/drogon_ctl/CMakeLists.txt @@ -1,5 +1,22 @@ -link_libraries(drogon) +link_libraries(drogon trantor uuid pthread jsoncpp dl z) +if(OpenSSL_FOUND) +link_libraries(ssl crypto) +endif() + AUX_SOURCE_DIRECTORY(. SRC_DIR) -add_executable(drogon_ctl ${SRC_DIR}) -add_dependencies(drogon_ctl trantor makeVersion) +add_executable(_drogon_ctl ${SRC_DIR}) +FILE(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/templates/*.csp) +foreach(cspFile ${SCP_LIST}) + message(STATUS "cspFile:" ${cspFile}) + EXEC_PROGRAM(basename ARGS "-s .csp ${cspFile}" OUTPUT_VARIABLE classname) + message(STATUS "view classname:" ${classname}) + add_custom_command(OUTPUT ${classname}.h ${classname}.cc + COMMAND _drogon_ctl + ARGS create view ${cspFile} + DEPENDS ${cspFile} + VERBATIM ) + set(TEMPL_SRC ${TEMPL_SRC} ${classname}.cc) +endforeach() +add_executable(drogon_ctl ${SRC_DIR} ${TEMPL_SRC}) +add_dependencies(drogon_ctl trantor makeVersion _drogon_ctl) install(TARGETS drogon_ctl DESTINATION bin) diff --git a/drogon_ctl/create_project.cc b/drogon_ctl/create_project.cc index 9a5c4b44..2ffa4d59 100644 --- a/drogon_ctl/create_project.cc +++ b/drogon_ctl/create_project.cc @@ -1,4 +1,5 @@ #include "create_project.h" +#include #include #include #include @@ -201,81 +202,8 @@ static void newJsonFindFile(std::ofstream &jsonFile) 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" - " //log_level:\"DEBUG\" by default,options:\"TRACE\",\"DEBUG\",\"INFO\",\"WARN\"\n" - " //The TRACE level is only valid when built in DEBUG mode.\n" - " \"log_level\": \"DEBUG\"\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" - " //use_gzip:true by default,use gzip to compress the response body's content;\n" - " \"use_gzip\": true\n" - " }\n" - "}\n"; + auto resp=HttpResponse::newHttpViewResponse("config",drogon::HttpViewData()); + configFile << resp->getBody(); } void create_project::createProject(const std::string &projectName) { diff --git a/drogon_ctl/templates/config.csp b/drogon_ctl/templates/config.csp new file mode 100644 index 00000000..af61e473 --- /dev/null +++ b/drogon_ctl/templates/config.csp @@ -0,0 +1,94 @@ +/* This is a JSON format configuration file +*/ +{ + //ssl:the global ssl files setting + /* + "ssl": { + "cert": "../../trantor/trantor/tests/server.pem", + "key": "../../trantor/trantor/tests/server.pem" + }, + "listeners": [ + { + //address:ip address,0.0.0.0 by default + "address": "0.0.0.0", + //port:port number + "port": 80, + //https:if use https for security,false by default + "https": false + }, + { + "address": "0.0.0.0", + "port": 443, + "https": true, + //cert,key:cert file path and key file path,empty by default, + //if empty,use global setting + "cert": "", + "key": "" + } + ],*/ + "app": { + //threads_num:num of threads,1 by default + "threads_num": 16, + //enable_session:false by default + "enable_session": false, + "session_timeout": 0, + //document_root:Root path of HTTP document,defaut path is ./ + "document_root": "./", + /* file_types: + * HTTP download file types,The file types supported by drogon + * by default are "html", "js", "css", "xml", "xsl", "txt", "svg", + * "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg", + * "gif", "bmp", "ico", "icns", etc. */ + "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" + //"load_dynamic_views":true, + //dynamic_views_path: if the path isn't prefixed with / or ./, + //it will be relative path of document_root path + //"dynamic_views_path":["./views"], + //log:set log output,drogon output logs to stdout by default + "log": { + //log_path:log file path,empty by default,in which case,log will output to the stdout + "log_path": "./", + //logfile_base_name:log file base name,empty by default which means drogon will name logfile as + //drogon.log ... + "logfile_base_name": "", + //log_size_limit:100000000 bytes by default, + //When the log file size reaches "log_size_limit", the log file will be switched. + "log_size_limit": 100000000, + //log_level:"DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN" + //The TRACE level is only valid when built in DEBUG mode. + "log_level": "DEBUG" + }, + //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, + //use_sendfile:true by default,if ture,the program will + //use sendfile() system-call to send static file to client; + "use_sendfile": true, + //use_gzip:true by default,use gzip to compress the response body's content; + "use_gzip": true, + //static_files_cache_time:5 by default,the time in which static file response is cached, + //0 means cache forever,the negative value means no cache + "static_files_cache_time":5 + } +} diff --git a/lib/inc/drogon/HttpAppFramework.h b/lib/inc/drogon/HttpAppFramework.h index 3930d0c9..51467137 100755 --- a/lib/inc/drogon/HttpAppFramework.h +++ b/lib/inc/drogon/HttpAppFramework.h @@ -109,6 +109,8 @@ class HttpAppFramework : public trantor::NonCopyable virtual void enableSendfile(bool sendFile) = 0; virtual void enableGzip(bool useGzip) = 0; virtual bool useGzip() const = 0; + virtual void setStaticFilesCacheTime(int cacheTime) = 0; + virtual int staticFilesCacheTime() const = 0; private: virtual void registerHttpApiController(const std::string &pathPattern, diff --git a/lib/src/ConfigLoader.cc b/lib/src/ConfigLoader.cc index 5e603fd1..fba7ebfc 100644 --- a/lib/src/ConfigLoader.cc +++ b/lib/src/ConfigLoader.cc @@ -160,6 +160,8 @@ static void loadApp(const Json::Value &app) HttpAppFramework::instance().enableSendfile(useSendfile); auto useGzip = app.get("use_gzip", true).asBool(); HttpAppFramework::instance().enableGzip(useGzip); + auto staticFilesCacheTime=app.get("static_files_cache_time",5).asInt(); + HttpAppFramework::instance().setStaticFilesCacheTime(staticFilesCacheTime); } static void loadListeners(const Json::Value &listeners) { diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 3e0eb6c2..68227cad 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -1059,8 +1059,8 @@ void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpR resp->setStatusCode(HttpResponse::k200OK); - //cache the response for 5 seconds - resp->setExpiredTime(5); + //cache the response for 5 seconds by default + resp->setExpiredTime(_staticFilesCacheTime); _responseCacheMap->insert(filePath, resp, resp->expiredTime(), [=]() { std::lock_guard guard(_staticFilesCacheMutex); _staticFilesCache.erase(filePath); diff --git a/lib/src/HttpAppFrameworkImpl.h b/lib/src/HttpAppFrameworkImpl.h index e9117e94..c1d39fd0 100644 --- a/lib/src/HttpAppFrameworkImpl.h +++ b/lib/src/HttpAppFrameworkImpl.h @@ -74,6 +74,8 @@ class HttpAppFrameworkImpl : public HttpAppFramework virtual void enableSendfile(bool sendFile) override { _useSendfile = sendFile; } virtual void enableGzip(bool useGzip) override { _useGzip = useGzip; } virtual bool useGzip() const override { return _useGzip; } + virtual void setStaticFilesCacheTime(int cacheTime) override { _staticFilesCacheTime = cacheTime; } + virtual int staticFilesCacheTime() const override { return _staticFilesCacheTime; } virtual ~HttpAppFrameworkImpl() { //Destroy the following objects before _loop destruction @@ -191,7 +193,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework size_t _logfileSize = 100000000; bool _useSendfile = true; bool _useGzip = true; - + int _staticFilesCacheTime = 5; std::unordered_map> _staticFilesCache; std::mutex _staticFilesCacheMutex; }; From 0787f6392285b01d263d93dfd680a13d7dfef2f8 Mon Sep 17 00:00:00 2001 From: antao Date: Mon, 15 Oct 2018 14:01:18 +0800 Subject: [PATCH 4/8] Modify HttpAppFrameworkImpl for static files cache --- lib/src/HttpAppFrameworkImpl.cc | 35 +++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 68227cad..5213b892 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -729,6 +729,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: //make a copy auto newCachedResp = std::make_shared(*std::dynamic_pointer_cast(cachedResp)); newCachedResp->addCookie("JSESSIONID", session_id); + newCachedResp->setExpiredTime(-1); callback(newCachedResp); return; } @@ -737,9 +738,6 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: } std::shared_ptr resp = std::make_shared(); - if (needSetJsessionid) - resp->addCookie("JSESSIONID", session_id); - // pick a Content-Type for the file if (filetype == "html") resp->setContentTypeCode(CT_TEXT_HTML); @@ -783,6 +781,19 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: resp->setContentTypeCode(CT_APPLICATION_OCTET_STREAM); readSendFile(filePath, req, resp); + if (needSetJsessionid) + { + auto newCachedResp = resp; + if (resp->expiredTime() >= 0) + { + //make a copy + newCachedResp = std::make_shared(*std::dynamic_pointer_cast(resp)); + newCachedResp->setExpiredTime(-1); + } + newCachedResp->addCookie("JSESSIONID", session_id); + callback(newCachedResp); + return; + } callback(resp); return; } @@ -1060,14 +1071,18 @@ void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpR resp->setStatusCode(HttpResponse::k200OK); //cache the response for 5 seconds by default - resp->setExpiredTime(_staticFilesCacheTime); - _responseCacheMap->insert(filePath, resp, resp->expiredTime(), [=]() { - std::lock_guard guard(_staticFilesCacheMutex); - _staticFilesCache.erase(filePath); - }); + + if (_staticFilesCacheTime >= 0) { - std::lock_guard guard(_staticFilesCacheMutex); - _staticFilesCache[filePath] = resp; + resp->setExpiredTime(_staticFilesCacheTime); + _responseCacheMap->insert(filePath, resp, resp->expiredTime(), [=]() { + std::lock_guard guard(_staticFilesCacheMutex); + _staticFilesCache.erase(filePath); + }); + { + std::lock_guard guard(_staticFilesCacheMutex); + _staticFilesCache[filePath] = resp; + } } } From 944c9813932757ffa8d41467e2d01a881e3a064d Mon Sep 17 00:00:00 2001 From: antao Date: Mon, 15 Oct 2018 15:32:00 +0800 Subject: [PATCH 5/8] Modify framework for request header 'If-Modified-Since' (please refer to rfc2616-14.25) --- lib/src/HttpAppFrameworkImpl.cc | 68 +++++++++++++++++---------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 5213b892..5a32b695 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -709,6 +709,40 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: { LOG_INFO << "file query!"; std::string filePath = _rootPath + path; + std::shared_ptr resp = std::make_shared(); + + //check last modified time,rfc2616-14.25 + //If-Modified-Since: Mon, 15 Oct 2018 06:26:33 GMT + + if (_enableLastModify) + { + struct stat fileStat; + LOG_TRACE << "enabled LastModify"; + if (stat(filePath.c_str(), &fileStat) >= 0) + { + LOG_TRACE << "last modify time:" << fileStat.st_mtime; + struct tm tm1; + gmtime_r(&fileStat.st_mtime, &tm1); + std::string timeStr; + timeStr.resize(64); + auto len=strftime((char *)timeStr.data(), timeStr.size() , "%a, %d %b %Y %T GMT", &tm1); + timeStr.resize(len); + std::string modiStr = req->getHeader("If-Modified-Since"); + if (modiStr == timeStr && !modiStr.empty()) + { + LOG_TRACE << "not Modified!"; + resp->setStatusCode(HttpResponse::k304NotModified); + if (needSetJsessionid) + { + resp->addCookie("JSESSIONID", session_id); + } + callback(resp); + return; + } + resp->addHeader("Last-Modified", timeStr); + resp->addHeader("Expires", "Thu, 01 Jan 1970 00:00:00 GMT"); + } + } //find cached response HttpResponsePtr cachedResp; { @@ -736,7 +770,6 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: callback(cachedResp); return; } - std::shared_ptr resp = std::make_shared(); // pick a Content-Type for the file if (filetype == "html") @@ -781,6 +814,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: resp->setContentTypeCode(CT_APPLICATION_OCTET_STREAM); readSendFile(filePath, req, resp); + if (needSetJsessionid) { auto newCachedResp = resp; @@ -873,7 +907,6 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: } else { - LOG_ERROR << "can't find controller " << ctrlName; } }); @@ -1008,7 +1041,6 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpRequestPtr &req, const HttpResponsePtr &resp) { - //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) @@ -1019,34 +1051,6 @@ void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpR return; } - if (_enableLastModify) - { - struct stat fileStat; - LOG_TRACE << "enabled LastModify"; - if (stat(filePath.c_str(), &fileStat) >= 0) - { - LOG_TRACE << "last modify time:" << fileStat.st_mtime; - struct tm tm1; - gmtime_r(&fileStat.st_mtime, &tm1); - char timeBuf[64]; - asctime_r(&tm1, timeBuf); - std::string timeStr(timeBuf); - std::string::size_type len = timeStr.length(); - std::string lastModified = timeStr.substr(0, len - 1) + " GMT"; - - std::string modiStr = req->getHeader("If-Modified-Since"); - if (modiStr != "" && modiStr == lastModified) - { - LOG_TRACE << "not Modified!"; - resp->setStatusCode(HttpResponse::k304NotModified); - return; - } - resp->addHeader("Last-Modified", lastModified); - - resp->addHeader("Expires", "Thu, 01 Jan 1970 00:00:00 GMT"); - } - } - std::streambuf *pbuf = infile.rdbuf(); std::streamsize filesize = pbuf->pubseekoff(0, infile.end); pbuf->pubseekoff(0, infile.beg); // rewind @@ -1071,7 +1075,7 @@ void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpR resp->setStatusCode(HttpResponse::k200OK); //cache the response for 5 seconds by default - + if (_staticFilesCacheTime >= 0) { resp->setExpiredTime(_staticFilesCacheTime); From 4ac5544a54f946b7a7b8439a957ecc46c08609c5 Mon Sep 17 00:00:00 2001 From: antao Date: Mon, 15 Oct 2018 15:58:11 +0800 Subject: [PATCH 6/8] Optimize files caching --- drogon_ctl/templates/config.csp | 2 +- lib/src/HttpAppFrameworkImpl.cc | 83 ++++++++++++++++++++------------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/drogon_ctl/templates/config.csp b/drogon_ctl/templates/config.csp index af61e473..3e0585dc 100644 --- a/drogon_ctl/templates/config.csp +++ b/drogon_ctl/templates/config.csp @@ -87,7 +87,7 @@ "use_sendfile": true, //use_gzip:true by default,use gzip to compress the response body's content; "use_gzip": true, - //static_files_cache_time:5 by default,the time in which static file response is cached, + //static_files_cache_time:5 (seconds) by default,the time in which static file response is cached, //0 means cache forever,the negative value means no cache "static_files_cache_time":5 } diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 5a32b695..ae43bede 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -710,39 +710,6 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: LOG_INFO << "file query!"; std::string filePath = _rootPath + path; std::shared_ptr resp = std::make_shared(); - - //check last modified time,rfc2616-14.25 - //If-Modified-Since: Mon, 15 Oct 2018 06:26:33 GMT - - if (_enableLastModify) - { - struct stat fileStat; - LOG_TRACE << "enabled LastModify"; - if (stat(filePath.c_str(), &fileStat) >= 0) - { - LOG_TRACE << "last modify time:" << fileStat.st_mtime; - struct tm tm1; - gmtime_r(&fileStat.st_mtime, &tm1); - std::string timeStr; - timeStr.resize(64); - auto len=strftime((char *)timeStr.data(), timeStr.size() , "%a, %d %b %Y %T GMT", &tm1); - timeStr.resize(len); - std::string modiStr = req->getHeader("If-Modified-Since"); - if (modiStr == timeStr && !modiStr.empty()) - { - LOG_TRACE << "not Modified!"; - resp->setStatusCode(HttpResponse::k304NotModified); - if (needSetJsessionid) - { - resp->addCookie("JSESSIONID", session_id); - } - callback(resp); - return; - } - resp->addHeader("Last-Modified", timeStr); - resp->addHeader("Expires", "Thu, 01 Jan 1970 00:00:00 GMT"); - } - } //find cached response HttpResponsePtr cachedResp; { @@ -756,6 +723,56 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: } } } + + //check last modified time,rfc2616-14.25 + //If-Modified-Since: Mon, 15 Oct 2018 06:26:33 GMT + + if (_enableLastModify) + { + if (cachedResp) + { + if (cachedResp->getHeader("Last-Modified") == req->getHeader("If-Modified-Since")) + { + resp->setStatusCode(HttpResponse::k304NotModified); + if (needSetJsessionid) + { + resp->addCookie("JSESSIONID", session_id); + } + callback(resp); + return; + } + } + else + { + struct stat fileStat; + LOG_TRACE << "enabled LastModify"; + if (stat(filePath.c_str(), &fileStat) >= 0) + { + LOG_TRACE << "last modify time:" << fileStat.st_mtime; + struct tm tm1; + gmtime_r(&fileStat.st_mtime, &tm1); + std::string timeStr; + timeStr.resize(64); + auto len = strftime((char *)timeStr.data(), timeStr.size(), "%a, %d %b %Y %T GMT", &tm1); + timeStr.resize(len); + std::string modiStr = req->getHeader("If-Modified-Since"); + if (modiStr == timeStr && !modiStr.empty()) + { + LOG_TRACE << "not Modified!"; + resp->setStatusCode(HttpResponse::k304NotModified); + if (needSetJsessionid) + { + resp->addCookie("JSESSIONID", session_id); + } + callback(resp); + return; + } + resp->addHeader("Last-Modified", timeStr); + resp->addHeader("Expires", "Thu, 01 Jan 1970 00:00:00 GMT"); + } + } + } + if (cachedResp) { if (needSetJsessionid) From b81584ff2e00f462bc20585fc44fb10de2c7dd88 Mon Sep 17 00:00:00 2001 From: an-tao <20741618@qq.com> Date: Mon, 15 Oct 2018 16:13:45 +0800 Subject: [PATCH 7/8] Modify log output --- lib/src/HttpAppFrameworkImpl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index ae43bede..4fd57199 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -707,7 +707,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: transform(filetype.begin(), filetype.end(), filetype.begin(), tolower); if (_fileTypeSet.find(filetype) != _fileTypeSet.end()) { - LOG_INFO << "file query!"; + //LOG_INFO << "file query!"; std::string filePath = _rootPath + path; std::shared_ptr resp = std::make_shared(); //find cached response @@ -1059,7 +1059,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestPtr &req, const std:: void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpRequestPtr &req, const HttpResponsePtr &resp) { std::ifstream infile(filePath, std::ifstream::binary); - LOG_INFO << "send http file:" << filePath; + LOG_TRACE << "send http file:" << filePath; if (!infile) { From 5362de9a58a30cf5d35c0c550b49c244b5b27189 Mon Sep 17 00:00:00 2001 From: antao Date: Mon, 15 Oct 2018 16:17:13 +0800 Subject: [PATCH 8/8] Modify log output --- lib/src/HttpAppFrameworkImpl.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 4fd57199..62e14ad8 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -1085,7 +1085,6 @@ void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpR std::string str; str.resize(filesize); pbuf->sgetn(&str[0], filesize); - LOG_INFO << "file len:" << str.length(); resp->setBody(std::move(str)); }