From 5238da4df75f7c29522cfd1abdd2197e892e901f Mon Sep 17 00:00:00 2001 From: antao Date: Tue, 25 Sep 2018 18:07:29 +0800 Subject: [PATCH] Use sendfile() system-call --- config.json.example | 5 ++++- examples/simple_example/main.cc | 2 +- lib/inc/drogon/HttpAppFramework.h | 10 +++++++--- lib/src/ConfigLoader.cc | 4 +++- lib/src/HttpAppFrameworkImpl.cc | 28 ++++++++++++++++++---------- lib/src/HttpAppFrameworkImpl.h | 29 ++++++++++++++++------------- lib/src/HttpResponseImpl.cc | 19 +++++++++++++++++-- lib/src/HttpResponseImpl.h | 7 +++++++ lib/src/HttpServer.cc | 10 +++++++++- trantor | 2 +- 10 files changed, 83 insertions(+), 33 deletions(-) diff --git a/config.json.example b/config.json.example index 3c47d23d..12e39dde 100644 --- a/config.json.example +++ b/config.json.example @@ -62,6 +62,9 @@ //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 } } \ No newline at end of file diff --git a/examples/simple_example/main.cc b/examples/simple_example/main.cc index 3267b27d..b83c2182 100755 --- a/examples/simple_example/main.cc +++ b/examples/simple_example/main.cc @@ -83,7 +83,7 @@ int main() std::cout< &filters=std::vector())=0; - virtual void registerHttpApiController(const std::string &pathPattern, - const HttpApiBinderBasePtr &binder, - const std::vector &filters=std::vector())=0; template static void registerHttpApiMethod(const std::string &pathPattern, FUNCTION && function, @@ -109,5 +106,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 &filters=std::vector())=0; + }; } diff --git a/lib/src/ConfigLoader.cc b/lib/src/ConfigLoader.cc index 94e6c749..e34b918b 100644 --- a/lib/src/ConfigLoader.cc +++ b/lib/src/ConfigLoader.cc @@ -131,6 +131,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 +159,7 @@ static void loadSSL(const Json::Value &sslFiles) HttpAppFramework::instance().setSSLFiles(cert,key); } void ConfigLoader::load() { - std::cout<<_configJsonRoot<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(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() diff --git a/lib/src/HttpAppFrameworkImpl.h b/lib/src/HttpAppFrameworkImpl.h index 50fe778e..eb7f66f1 100644 --- a/lib/src/HttpAppFrameworkImpl.h +++ b/lib/src/HttpAppFrameworkImpl.h @@ -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 &filters= std::vector())override ; - virtual void registerHttpApiController(const std::string &pathPattern, - const HttpApiBinderBasePtr &binder, - const std::vector &filters=std::vector()) 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;} @@ -69,14 +66,19 @@ namespace drogon 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 &filters=std::vector()) override ; + std::vector> _listeners; void onAsyncRequest(const HttpRequestPtr& req,const std::function & callback); void onNewWebsockRequest(const HttpRequestPtr& req, @@ -104,11 +106,11 @@ namespace drogon const std::string &session_id, const std::function &missCallback); void doFilterChain(const std::shared_ptr>> &chain, - const HttpRequestPtr& req, - const std::function & callback, - bool needSetJsessionid, - const std::string &session_id, - const std::function &missCallback); + const HttpRequestPtr& req, + const std::function & callback, + bool needSetJsessionid, + const std::string &session_id, + const std::function &missCallback); // struct ControllerAndFiltersName { @@ -169,5 +171,6 @@ namespace drogon std::string _logPath=""; std::string _logfileBaseName=""; size_t _logfileSize=100000000; + bool _useSendfile=true; }; } diff --git a/lib/src/HttpResponseImpl.cc b/lib/src/HttpResponseImpl.cc index f3354f3c..eed9fb30 100755 --- a/lib/src/HttpResponseImpl.cc +++ b/lib/src/HttpResponseImpl.cc @@ -27,6 +27,7 @@ #include #include #include +#include using namespace trantor; using namespace drogon; @@ -313,12 +314,26 @@ 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; + } + snprintf(buf, sizeof buf, "Content-Length: %lld\r\n",filestat.st_size); + } + output->append(buf); if(_headers.find("Connection")==_headers.end()) { diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index 5787d2cd..01179962 100755 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -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 _jsonPtr; //trantor::Date receiveTime_; diff --git a/lib/src/HttpServer.cc b/lib/src/HttpServer.cc index cc7970b5..5da3407e 100755 --- a/lib/src/HttpServer.cc +++ b/lib/src/HttpServer.cc @@ -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()(response)->sendfileName(); + + if(sendfileName.empty()&& + response->getContentTypeCode()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(response)->appendToBuffer(&buf); conn->send(std::move(buf)); + auto & sendfileName=std::dynamic_pointer_cast(response)->sendfileName(); + if(!sendfileName.empty()) + { + conn->sendFile(sendfileName.c_str()); + } if (response->closeConnection()) { conn->shutdown(); } diff --git a/trantor b/trantor index 87aa3947..19693734 160000 --- a/trantor +++ b/trantor @@ -1 +1 @@ -Subproject commit 87aa3947909b8b8eeb67066eb1491d7a26f83a9e +Subproject commit 19693734fa9b8e48887a255368b289542adb43f7