fix: make HTTP date formatting locale-independent#2486
fix: make HTTP date formatting locale-independent#2486an-tao merged 3 commits intodrogonframework:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR makes Drogon’s HTTP-date formatting locale-independent so Date: response headers (and cookie Expires= values) stay RFC-compliant under non-C locales by bypassing strftime/stream locale paths.
Changes:
- Added a dedicated
formatHttpDate()formatter that uses fixed English weekday/month names +snprintf. - Updated
getHttpFullDate()andgetHttpFullDateStr()to route through the dedicated formatter (reducing locale and stream overhead). - Removed unused code in
dateToCustomFormattedString().
Comments suppressed due to low confidence (1)
lib/src/Utilities.cc:1063
lastSecondis initialized to0, so the very first call with adatethat falls exactly on Unix epoch second 0 (or any value equal to the initializer) will hit the cache path and return an uninitialized/emptylastTimeStringwithout formatting. Consider initializinglastSecondto a sentinel that cannot be a real timestamp (e.g.,std::numeric_limits<int64_t>::min()) so the first call always formats.
char *getHttpFullDate(const trantor::Date &date)
{
static thread_local int64_t lastSecond = 0;
static thread_local char lastTimeString[128] = {0};
auto nowSecond =
date.microSecondsSinceEpoch() / trantor::Date::MICRO_SECONDS_PER_SEC;
if (nowSecond == lastSecond)
{
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const std::string &getHttpFullDateStr(const trantor::Date &date) | ||
| { | ||
| static thread_local int64_t lastSecond = 0; | ||
| static thread_local std::string lastTimeString(128, 0); | ||
| static thread_local std::string lastTimeString; | ||
| auto nowSecond = | ||
| date.microSecondsSinceEpoch() / trantor::Date::MICRO_SECONDS_PER_SEC; | ||
| if (nowSecond == lastSecond) | ||
| { | ||
| return lastTimeString; | ||
| } |
There was a problem hiding this comment.
Same caching initializer issue as getHttpFullDate(): lastSecond starts at 0, so the first call where nowSecond == 0 (e.g., formatting Thu, 01 Jan 1970 00:00:00 GMT, commonly used for cookie deletion) returns lastTimeString without formatting. Initialize lastSecond to a sentinel value (e.g., std::numeric_limits<int64_t>::min()) so the first call always formats.
This PR follows up on the locale-related issue previously addressed in drogon PR #2217, which did not cover all runtime paths.
Some response rendering paths still call getHttpFullDate(), which relies on trantor::Date::toCustomFormattedString() (strftime, process-locale dependent).
As a result, the HTTP Date header can still be localized under non-C locales (I ran into this issue with a
Date:header under a German locale).This PR makes all Drogon's HTTP date generation utilities locale-independent by routing both
getHttpFullDate()andgetHttpFullDateStr()through a single static dedicated formatter (formatHttpDate, which uses fixed English weekday/month names and snprintf.This also avoids stream/locale formatting overhead in this hot path.
Notes
drogon::utils::getHttpDate()remains locale-sensitive and may fail under non-C/ non-English locales.There is currently no locale-safe HTTP date parser in Drogon.
A robust fix would be to add a dedicated locale-independent parser, potentially adapted from proven implementations such as nginx or libcurl.
trantor::Date::toCustomFormattedString()is also supposed to be locale-sensitive, then the proper long-term fix should be to fix thestrftimeusage in trantor itself.In that case, it would be preferable to simplify drogon by reverting the workaround-style changes introduced in these two PRs and relying on the corrected trantor behavior as the single source of truth.