From ba7d788184fe139b738f3561a40e1f810e8019bf Mon Sep 17 00:00:00 2001 From: An Tao Date: Wed, 31 Jul 2019 19:44:32 +0800 Subject: [PATCH] Add options for 'Server' header and 'Date' header (#202) --- config.example.json | 11 ++-- drogon_ctl/templates/config.csp | 11 ++-- examples/simple_example/TimeFilter.cc | 4 +- lib/inc/drogon/HttpAppFramework.h | 10 ++++ lib/src/ConfigLoader.cc | 4 ++ lib/src/HttpAppFrameworkImpl.h | 18 +++++++ lib/src/HttpResponseImpl.cc | 76 ++++++++++++++++++--------- lib/src/MultiPart.cc | 3 +- lib/src/StaticFileRouter.cc | 3 +- 9 files changed, 103 insertions(+), 37 deletions(-) mode change 100755 => 100644 examples/simple_example/TimeFilter.cc diff --git a/config.example.json b/config.example.json index 179a0df4..3969d2f9 100644 --- a/config.example.json +++ b/config.example.json @@ -142,9 +142,15 @@ //idle_connection_timeout: Defaults to 60 seconds, the lifetime //of the connection without read or write "idle_connection_timeout": 60, - //server_header_field: Set the 'server' header field in each response sent by drogon, - //empty string by default with which the 'server' header field is set to "Server: drogon/version string\r\n" + //server_header_field: Set the 'Server' header field in each response sent by drogon, + //empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n" "server_header_field": "", + //enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default + //value is true. + "enable_server_header": true, + //enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default + //value is true. + "enable_date_header": true, //keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection. //After the maximum number of requests are made, the connection is closed. //The default value of 0 means no limit. @@ -179,7 +185,6 @@ "config": { "heartbeat_interval": 2 } - }], //custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. "custom_config": {} diff --git a/drogon_ctl/templates/config.csp b/drogon_ctl/templates/config.csp index 3be2c968..7aaeee3c 100644 --- a/drogon_ctl/templates/config.csp +++ b/drogon_ctl/templates/config.csp @@ -142,9 +142,15 @@ //idle_connection_timeout: Defaults to 60 seconds, the lifetime //of the connection without read or write "idle_connection_timeout": 60, - //server_header_field: Set the 'server' header field in each response sent by drogon, - //empty string by default with which the 'server' header field is set to "Server: drogon/version string\r\n" + //server_header_field: Set the 'Server' header field in each response sent by drogon, + //empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n" "server_header_field": "", + //enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default + //value is true. + "enable_server_header": true, + //enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default + //value is true. + "enable_date_header": true, //keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection. //After the maximum number of requests are made, the connection is closed. //The default value of 0 means no limit. @@ -179,7 +185,6 @@ "config": { "heartbeat_interval": 2 } - }], //custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. "custom_config": {} diff --git a/examples/simple_example/TimeFilter.cc b/examples/simple_example/TimeFilter.cc old mode 100755 new mode 100644 index 3b8f110f..ebb3abe6 --- a/examples/simple_example/TimeFilter.cc +++ b/examples/simple_example/TimeFilter.cc @@ -11,7 +11,7 @@ void TimeFilter::doFilter(const HttpRequestPtr &req, trantor::Date now = trantor::Date::date(); if (!req->session()) { - //no session support by framework,pls enable session + // no session support by framework,pls enable session auto resp = HttpResponse::newNotFoundResponse(); cb(resp); return; @@ -24,7 +24,7 @@ void TimeFilter::doFilter(const HttpRequestPtr &req, LOG_TRACE << "update visitDate"; if (now > lastDate.after(10)) { - //10 sec later can visit again; + // 10 sec later can visit again; ccb(); return; } diff --git a/lib/inc/drogon/HttpAppFramework.h b/lib/inc/drogon/HttpAppFramework.h index f6a45b12..b4e502e6 100644 --- a/lib/inc/drogon/HttpAppFramework.h +++ b/lib/inc/drogon/HttpAppFramework.h @@ -714,6 +714,16 @@ class HttpAppFramework : public trantor::NonCopyable */ virtual void setServerHeaderField(const std::string &server) = 0; + /// Control if the 'Server' header or the 'Date' header is added to each + /// HTTP response. + /** + * NOTE: + * These operations can be performed by options in the configuration file. + * The headers are sent to clients by default. + */ + virtual void enableServerHeader(bool flag) = 0; + virtual void enableDateHeader(bool flag) = 0; + /// Set the maximum number of requests that can be served through one /// keep-alive connection. /** diff --git a/lib/src/ConfigLoader.cc b/lib/src/ConfigLoader.cc index fdb6c745..27e5a2a1 100644 --- a/lib/src/ConfigLoader.cc +++ b/lib/src/ConfigLoader.cc @@ -301,6 +301,10 @@ static void loadApp(const Json::Value &app) auto server = app.get("server_header_field", "").asString(); if (!server.empty()) drogon::app().setServerHeaderField(server); + auto sendServerHeader = app.get("enable_server_header", true).asBool(); + drogon::app().enableServerHeader(sendServerHeader); + auto sendDateHeader = app.get("enable_date_header", true).asBool(); + drogon::app().enableDateHeader(sendDateHeader); auto keepaliveReqs = app.get("keepalive_requests", 0).asUInt64(); drogon::app().setKeepaliveRequestsNumber(keepaliveReqs); auto pipeliningReqs = app.get("pipelining_requests", 0).asUInt64(); diff --git a/lib/src/HttpAppFrameworkImpl.h b/lib/src/HttpAppFrameworkImpl.h index 8d76293a..275ed1de 100644 --- a/lib/src/HttpAppFrameworkImpl.h +++ b/lib/src/HttpAppFrameworkImpl.h @@ -338,6 +338,22 @@ class HttpAppFrameworkImpl : public HttpAppFramework _serverHeader = "Server: " + server + "\r\n"; } + virtual void enableServerHeader(bool flag) override + { + _enableServerHeader = flag; + } + virtual void enableDateHeader(bool flag) override + { + _enableDateHeader = flag; + } + bool sendServerHeader() const + { + return _enableServerHeader; + } + bool sendDateHeader() const + { + return _enableDateHeader; + } const std::string &getServerHeaderString() const { return _serverHeader; @@ -460,6 +476,8 @@ class HttpAppFrameworkImpl : public HttpAppFramework HttpResponsePtr _custom404; orm::DbClientManager _dbClientManager; static InitBeforeMainFunction _initFirst; + bool _enableServerHeader = true; + bool _enableDateHeader = true; std::vector> _newConnectionAdvices; diff --git a/lib/src/HttpResponseImpl.cc b/lib/src/HttpResponseImpl.cc index c5ab2d30..d16c01d7 100644 --- a/lib/src/HttpResponseImpl.cc +++ b/lib/src/HttpResponseImpl.cc @@ -191,34 +191,46 @@ void HttpResponseImpl::makeHeaderString( headerStringPtr->append(it->second); headerStringPtr->append("\r\n"); } - headerStringPtr->append( - HttpAppFrameworkImpl::instance().getServerHeaderString()); + if (HttpAppFrameworkImpl::instance().sendServerHeader()) + { + headerStringPtr->append( + HttpAppFrameworkImpl::instance().getServerHeaderString()); + } } std::shared_ptr HttpResponseImpl::renderToString() const { if (_expriedTime >= 0) { - if (_datePos != std::string::npos) + if (drogon::HttpAppFrameworkImpl::instance().sendDateHeader()) { - auto now = trantor::Date::now(); - bool isDateChanged = ((now.microSecondsSinceEpoch() / - MICRO_SECONDS_PRE_SEC) != _httpStringDate); - assert(_httpString); - if (isDateChanged) + if (_datePos != std::string::npos) { - _httpStringDate = - now.microSecondsSinceEpoch() / MICRO_SECONDS_PRE_SEC; - auto newDate = utils::getHttpFullDate(now); + auto now = trantor::Date::now(); + bool isDateChanged = + ((now.microSecondsSinceEpoch() / MICRO_SECONDS_PRE_SEC) != + _httpStringDate); + assert(_httpString); + if (isDateChanged) + { + _httpStringDate = + now.microSecondsSinceEpoch() / MICRO_SECONDS_PRE_SEC; + auto newDate = utils::getHttpFullDate(now); + + _httpString = std::make_shared(*_httpString); + memcpy((void *)&(*_httpString)[_datePos], + newDate, + strlen(newDate)); + return _httpString; + } - _httpString = std::make_shared(*_httpString); - memcpy((void *)&(*_httpString)[_datePos], - newDate, - strlen(newDate)); return _httpString; } - - return _httpString; + } + else + { + if (_httpString) + return _httpString; } } auto httpString = std::make_shared(); @@ -242,16 +254,23 @@ std::shared_ptr HttpResponseImpl::renderToString() const } // output Date header - httpString->append("Date: "); - auto datePos = httpString->length(); - httpString->append(utils::getHttpFullDate(trantor::Date::date())); - httpString->append("\r\n\r\n"); + if (drogon::HttpAppFrameworkImpl::instance().sendDateHeader()) + { + httpString->append("Date: "); + auto datePos = httpString->length(); + httpString->append(utils::getHttpFullDate(trantor::Date::date())); + httpString->append("\r\n\r\n"); + _datePos = datePos; + } + else + { + httpString->append("\r\n"); + } LOG_TRACE << "reponse(no body):" << httpString->c_str(); httpString->append(*_bodyPtr); if (_expriedTime >= 0) { - _datePos = datePos; _httpString = httpString; } return httpString; @@ -280,9 +299,16 @@ std::shared_ptr HttpResponseImpl::renderHeaderForHeadMethod() const } // output Date header - httpString->append("Date: "); - httpString->append(utils::getHttpFullDate(trantor::Date::date())); - httpString->append("\r\n\r\n"); + if (drogon::HttpAppFrameworkImpl::instance().sendDateHeader()) + { + httpString->append("Date: "); + httpString->append(utils::getHttpFullDate(trantor::Date::date())); + httpString->append("\r\n\r\n"); + } + else + { + httpString->append("\r\n"); + } return httpString; } diff --git a/lib/src/MultiPart.cc b/lib/src/MultiPart.cc index 2e048712..74fd8fb5 100644 --- a/lib/src/MultiPart.cc +++ b/lib/src/MultiPart.cc @@ -47,8 +47,7 @@ int MultiPartParser::parse(const HttpRequestPtr &req) if (req->method() != Post) return -1; const std::string &contentType = - static_cast(req.get())->getHeaderBy( - "content-type"); + static_cast(req.get())->getHeaderBy("content-type"); if (contentType.empty()) { return -1; diff --git a/lib/src/StaticFileRouter.cc b/lib/src/StaticFileRouter.cc index 1c68290a..49435173 100644 --- a/lib/src/StaticFileRouter.cc +++ b/lib/src/StaticFileRouter.cc @@ -124,8 +124,7 @@ void StaticFileRouter::route( { // make a copy auto newCachedResp = std::make_shared( - *static_cast( - cachedResp.get())); + *static_cast(cachedResp.get())); newCachedResp->addCookie("JSESSIONID", sessionId); newCachedResp->setExpiredTime(-1); callback(newCachedResp);