From 3e944d28d8c6c438a69d15e8ede6f986126e217f Mon Sep 17 00:00:00 2001 From: EasyMoney322 <20065717+easymoney322@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:08:24 +0500 Subject: [PATCH] Fixed issues created by the date string being localized (#2217) --- .github/workflows/cmake.yml | 6 +++++ lib/inc/drogon/utils/Utilities.h | 6 +++++ lib/src/Cookie.cc | 2 +- lib/src/HttpResponseImpl.cc | 6 ++--- lib/src/Utilities.cc | 29 +++++++++++++++++++++++ lib/tests/integration_test/server/main.cc | 3 +-- lib/tests/unittests/HttpFullDateTest.cc | 2 +- 7 files changed, 46 insertions(+), 8 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 2bd213c19a..135f395937 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -173,6 +173,12 @@ jobs: sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev sudo apt-get install -y ninja-build libbrotli-dev sudo apt-get install -y libspdlog-dev + + - name: Install and update locales + run: | + sudo apt-get install -y locales + sudo locale-gen en_US && sudo locale-gen en_US.UTF-8 + sudo update-locale LC_ALL=en_US.UTF-8 - name: Install postgresql run: | diff --git a/lib/inc/drogon/utils/Utilities.h b/lib/inc/drogon/utils/Utilities.h index dc0f71837c..1630cf43e0 100644 --- a/lib/inc/drogon/utils/Utilities.h +++ b/lib/inc/drogon/utils/Utilities.h @@ -301,6 +301,12 @@ DROGON_EXPORT std::string brotliDecompress(const char *data, DROGON_EXPORT char *getHttpFullDate( const trantor::Date &date = trantor::Date::now()); +DROGON_EXPORT const std::string &getHttpFullDateStr( + const trantor::Date &date = trantor::Date::now()); + +DROGON_EXPORT void dateToCustomFormattedString(const std::string &fmtStr, + std::string &str, + const trantor::Date &date); /// Get the trantor::Date object according to the http full date string /** * Returns trantor::Date(std::numeric_limits::max()) upon failure. diff --git a/lib/src/Cookie.cc b/lib/src/Cookie.cc index 1b1c0e0a63..b46caeb04d 100644 --- a/lib/src/Cookie.cc +++ b/lib/src/Cookie.cc @@ -30,7 +30,7 @@ std::string Cookie::cookieString() const expiresDate_.microSecondsSinceEpoch() >= 0) { ret.append("Expires=") - .append(utils::getHttpFullDate(expiresDate_)) + .append(utils::getHttpFullDateStr(expiresDate_)) .append("; "); } if (maxAge_.has_value()) diff --git a/lib/src/HttpResponseImpl.cc b/lib/src/HttpResponseImpl.cc index 5716e34e1a..5e9e46f1b7 100644 --- a/lib/src/HttpResponseImpl.cc +++ b/lib/src/HttpResponseImpl.cc @@ -632,8 +632,7 @@ void HttpResponseImpl::renderToBuffer(trantor::MsgBuffer &buffer) drogon::HttpAppFrameworkImpl::instance().sendDateHeader()) { buffer.append("date: "); - buffer.append(utils::getHttpFullDate(trantor::Date::date()), - httpFullDateStringLength); + buffer.append(utils::getHttpFullDateStr(trantor::Date::date())); buffer.append("\r\n\r\n"); } else @@ -706,8 +705,7 @@ std::shared_ptr HttpResponseImpl::renderToBuffer() { httpString->append("date: "); auto datePos = httpString->readableBytes(); - httpString->append(utils::getHttpFullDate(trantor::Date::date()), - httpFullDateStringLength); + httpString->append(utils::getHttpFullDateStr(trantor::Date::date())); httpString->append("\r\n\r\n"); datePos_ = datePos; } diff --git a/lib/src/Utilities.cc b/lib/src/Utilities.cc index 4020c34ae9..7c6130237b 100644 --- a/lib/src/Utilities.cc +++ b/lib/src/Utilities.cc @@ -1034,6 +1034,35 @@ char *getHttpFullDate(const trantor::Date &date) return lastTimeString; } +void dateToCustomFormattedString(const std::string &fmtStr, + std::string &str, + const trantor::Date &date) +{ + auto nowSecond = date.microSecondsSinceEpoch() / MICRO_SECONDS_PRE_SEC; + time_t seconds = static_cast(nowSecond); + struct tm tm_LValue = date.tmStruct(); + std::stringstream Out; + Out.imbue(std::locale{"en_US"}); + Out << std::put_time(&tm_LValue, fmtStr.c_str()); + str = Out.str(); +} + +const std::string &getHttpFullDateStr(const trantor::Date &date) +{ + static thread_local int64_t lastSecond = 0; + static thread_local std::string lastTimeString(128, 0); + auto nowSecond = date.microSecondsSinceEpoch() / MICRO_SECONDS_PRE_SEC; + if (nowSecond == lastSecond) + { + return lastTimeString; + } + lastSecond = nowSecond; + dateToCustomFormattedString("%a, %d %b %Y %H:%M:%S GMT", + lastTimeString, + date); + return lastTimeString; +} + trantor::Date getHttpDate(const std::string &httpFullDateString) { static const std::array formats = { diff --git a/lib/tests/integration_test/server/main.cc b/lib/tests/integration_test/server/main.cc index 39900f94d6..d8e04e1994 100644 --- a/lib/tests/integration_test/server/main.cc +++ b/lib/tests/integration_test/server/main.cc @@ -408,8 +408,7 @@ int main() app().registerCustomExtensionMime("md", "text/markdown"); app().setFileTypes({"md", "html", "jpg", "cc", "txt"}); std::cout << "Date: " - << std::string{drogon::utils::getHttpFullDate( - trantor::Date::now())} + << drogon::utils::getHttpFullDateStr(trantor::Date::now()) << std::endl; app().registerBeginningAdvice( diff --git a/lib/tests/unittests/HttpFullDateTest.cc b/lib/tests/unittests/HttpFullDateTest.cc index 6b891bf0f0..fea62ca2ae 100644 --- a/lib/tests/unittests/HttpFullDateTest.cc +++ b/lib/tests/unittests/HttpFullDateTest.cc @@ -7,7 +7,7 @@ using namespace drogon; DROGON_TEST(HttpFullDateTest) { - auto str = utils::getHttpFullDate(); + auto str = utils::getHttpFullDateStr(); auto date = utils::getHttpDate(str); CHECK(utils::getHttpFullDate(date) == str); }