nghttpx: Use pre-allocated buffer for timestamp string

This commit is contained in:
Tatsuhiro Tsujikawa 2016-10-10 22:02:48 +09:00
parent 00a8c378d4
commit 1a37044d3c
10 changed files with 130 additions and 54 deletions

View File

@ -1488,7 +1488,7 @@ int Http2Upstream::error_reply(Downstream *downstream,
auto response_status = http2::stringify_status(balloc, status_code); auto response_status = http2::stringify_status(balloc, status_code);
auto content_length = util::make_string_ref_uint(balloc, html.size()); auto content_length = util::make_string_ref_uint(balloc, html.size());
auto date = make_string_ref(balloc, StringRef{lgconf->time_http_str}); auto date = make_string_ref(balloc, lgconf->time_http);
auto nva = std::array<nghttp2_nv, 5>{ auto nva = std::array<nghttp2_nv, 5>{
{http2::make_nv_ls_nocopy(":status", response_status), {http2::make_nv_ls_nocopy(":status", response_status),

View File

@ -933,8 +933,7 @@ void HttpsUpstream::error_reply(unsigned int status_code) {
output->append("\r\nDate: "); output->append("\r\nDate: ");
auto lgconf = log_config(); auto lgconf = log_config();
lgconf->update_tstamp(std::chrono::system_clock::now()); lgconf->update_tstamp(std::chrono::system_clock::now());
auto &date = lgconf->time_http_str; output->append(lgconf->time_http);
output->append(date);
output->append("\r\nContent-Type: text/html; " output->append("\r\nContent-Type: text/html; "
"charset=UTF-8\r\nConnection: close\r\n\r\n"); "charset=UTF-8\r\nConnection: close\r\n\r\n");
output->append(html); output->append(html);

View File

@ -138,7 +138,7 @@ Log::~Log() {
auto tty = lgconf->errorlog_tty; auto tty = lgconf->errorlog_tty;
lgconf->update_tstamp(std::chrono::system_clock::now()); lgconf->update_tstamp(std::chrono::system_clock::now());
auto &time_local = lgconf->time_local_str; auto &time_local = lgconf->time_local;
if (severity_ == NOTICE) { if (severity_ == NOTICE) {
rv = rv =
@ -255,8 +255,8 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
auto avail = sizeof(buf) - 2; auto avail = sizeof(buf) - 2;
lgconf->update_tstamp(lgsp.time_now); lgconf->update_tstamp(lgsp.time_now);
auto &time_local = lgconf->time_local_str; auto &time_local = lgconf->time_local;
auto &time_iso8601 = lgconf->time_iso8601_str; auto &time_iso8601 = lgconf->time_iso8601;
for (auto &lf : lfv) { for (auto &lf : lfv) {
switch (lf.type) { switch (lf.type) {

View File

@ -62,9 +62,9 @@ void LogConfig::update_tstamp(
time_str_updated_ = now; time_str_updated_ = now;
time_local_str = util::format_common_log(now); time_local = util::format_common_log(time_local_buf.data(), now);
time_iso8601_str = util::format_iso8601(now); time_iso8601 = util::format_iso8601(time_iso8601_buf.data(), now);
time_http_str = util::format_http_date(now); time_http = util::format_http_date(time_http_buf.data(), now);
} }
} // namespace shrpx } // namespace shrpx

View File

@ -29,13 +29,20 @@
#include <chrono> #include <chrono>
#include "template.h"
using namespace nghttp2;
namespace shrpx { namespace shrpx {
struct LogConfig { struct LogConfig {
std::chrono::system_clock::time_point time_str_updated_; std::chrono::system_clock::time_point time_str_updated_;
std::string time_local_str; std::array<char, sizeof("03/Jul/2014:00:19:38 +0900")> time_local_buf;
std::string time_iso8601_str; std::array<char, sizeof("2014-11-15T12:58:24.741+09:00")> time_iso8601_buf;
std::string time_http_str; std::array<char, sizeof("Mon, 10 Oct 2016 10:25:58 GMT")> time_http_buf;
StringRef time_local;
StringRef time_iso8601;
StringRef time_http;
int accesslog_fd; int accesslog_fd;
int errorlog_fd; int errorlog_fd;
// true if errorlog_fd is referring to a terminal. // true if errorlog_fd is referring to a terminal.

View File

@ -230,9 +230,8 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
if (!date) { if (!date) {
auto lgconf = log_config(); auto lgconf = log_config();
lgconf->update_tstamp(std::chrono::system_clock::now()); lgconf->update_tstamp(std::chrono::system_clock::now());
resp.fs.add_header_token( resp.fs.add_header_token(StringRef::from_lit("date"),
StringRef::from_lit("date"), make_string_ref(balloc, lgconf->time_http), false,
make_string_ref(balloc, StringRef{lgconf->time_http_str}), false,
http2::HD_DATE); http2::HD_DATE);
} }

View File

@ -1003,8 +1003,8 @@ int SpdyUpstream::error_reply(Downstream *downstream,
const char *nv[] = {":status", status_string.c_str(), ":version", "http/1.1", const char *nv[] = {":status", status_string.c_str(), ":version", "http/1.1",
"content-type", "text/html; charset=UTF-8", "server", "content-type", "text/html; charset=UTF-8", "server",
get_config()->http.server_name.c_str(), "content-length", get_config()->http.server_name.c_str(), "content-length",
content_length.c_str(), "date", content_length.c_str(), "date", lgconf->time_http.c_str(),
lgconf->time_http_str.c_str(), nullptr}; nullptr};
rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv, rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv,
&data_prd); &data_prd);

View File

@ -211,17 +211,20 @@ const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
} // namespace } // namespace
std::string http_date(time_t t) { std::string http_date(time_t t) {
/* Sat, 27 Sep 2014 06:31:15 GMT */
std::string res(29, 0);
http_date(&res[0], t);
return res;
}
char *http_date(char *res, time_t t) {
struct tm tms; struct tm tms;
std::string res;
if (gmtime_r(&t, &tms) == nullptr) { if (gmtime_r(&t, &tms) == nullptr) {
return res; return res;
} }
/* Sat, 27 Sep 2014 06:31:15 GMT */ auto p = res;
res.resize(29);
auto p = std::begin(res);
auto s = DAY_OF_WEEK[tms.tm_wday]; auto s = DAY_OF_WEEK[tms.tm_wday];
p = std::copy_n(s, 3, p); p = std::copy_n(s, 3, p);
@ -242,22 +245,24 @@ std::string http_date(time_t t) {
s = " GMT"; s = " GMT";
p = std::copy_n(s, 4, p); p = std::copy_n(s, 4, p);
return res; return p;
} }
std::string common_log_date(time_t t) { std::string common_log_date(time_t t) {
// 03/Jul/2014:00:19:38 +0900
std::string res(26, 0);
common_log_date(&res[0], t);
return res;
}
char *common_log_date(char *res, time_t t) {
struct tm tms; struct tm tms;
if (localtime_r(&t, &tms) == nullptr) { if (localtime_r(&t, &tms) == nullptr) {
return ""; return res;
} }
// Format data like this: auto p = res;
// 03/Jul/2014:00:19:38 +0900
std::string res;
res.resize(26);
auto p = std::begin(res);
p = cpydig(p, tms.tm_mday, 2); p = cpydig(p, tms.tm_mday, 2);
*p++ = '/'; *p++ = '/';
@ -288,24 +293,27 @@ std::string common_log_date(time_t t) {
p = cpydig(p, gmtoff / 3600, 2); p = cpydig(p, gmtoff / 3600, 2);
p = cpydig(p, (gmtoff % 3600) / 60, 2); p = cpydig(p, (gmtoff % 3600) / 60, 2);
return res; return p;
} }
std::string iso8601_date(int64_t ms) { std::string iso8601_date(int64_t ms) {
// 2014-11-15T12:58:24.741Z
// 2014-11-15T12:58:24.741+09:00
std::string res(29, 0);
auto p = iso8601_date(&res[0], ms);
res.resize(p - &res[0]);
return res;
}
char *iso8601_date(char *res, int64_t ms) {
time_t sec = ms / 1000; time_t sec = ms / 1000;
tm tms; tm tms;
if (localtime_r(&sec, &tms) == nullptr) { if (localtime_r(&sec, &tms) == nullptr) {
return ""; return res;
} }
// Format data like this: auto p = res;
// 2014-11-15T12:58:24.741Z
// 2014-11-15T12:58:24.741+09:00
std::string res;
res.resize(29);
auto p = std::begin(res);
p = cpydig(p, tms.tm_year + 1900, 4); p = cpydig(p, tms.tm_year + 1900, 4);
*p++ = '-'; *p++ = '-';
@ -340,9 +348,7 @@ std::string iso8601_date(int64_t ms) {
p = cpydig(p, (gmtoff % 3600) / 60, 2); p = cpydig(p, (gmtoff % 3600) / 60, 2);
} }
res.resize(p - std::begin(res)); return p;
return res;
} }
time_t parse_http_date(const StringRef &s) { time_t parse_http_date(const StringRef &s) {

View File

@ -147,15 +147,29 @@ template <size_t N> std::string format_hex(const std::array<uint8_t, N> &s) {
StringRef format_hex(BlockAllocator &balloc, const StringRef &s); StringRef format_hex(BlockAllocator &balloc, const StringRef &s);
// Returns given time |t| from epoch in HTTP Date format (e.g., Mon,
// 10 Oct 2016 10:25:58 GMT).
std::string http_date(time_t t); std::string http_date(time_t t);
// Writes given time |t| from epoch in HTTP Date format into the
// buffer pointed by |res|. The buffer must be at least 29 bytes
// long. This function returns the one beyond the last position.
char *http_date(char *res, time_t t);
// Returns given time |t| from epoch in Common Log format (e.g., // Returns given time |t| from epoch in Common Log format (e.g.,
// 03/Jul/2014:00:19:38 +0900) // 03/Jul/2014:00:19:38 +0900)
std::string common_log_date(time_t t); std::string common_log_date(time_t t);
// Writes given time |t| from epoch in Common Log format into the
// buffer pointed by |res|. The buffer must be at least 26 bytes
// long. This function returns the one beyond the last position.
char *common_log_date(char *res, time_t t);
// Returns given millisecond |ms| from epoch in ISO 8601 format (e.g., // Returns given millisecond |ms| from epoch in ISO 8601 format (e.g.,
// 2014-11-15T12:58:24.741Z) // 2014-11-15T12:58:24.741Z or 2014-11-15T12:58:24.741+09:00)
std::string iso8601_date(int64_t ms); std::string iso8601_date(int64_t ms);
// Writes given time |t| from epoch in ISO 8601 format into the buffer
// pointed by |res|. The buffer must be at least 29 bytes long. This
// function returns the one beyond the last position.
char *iso8601_date(char *res, int64_t ms);
time_t parse_http_date(const StringRef &s); time_t parse_http_date(const StringRef &s);
@ -536,29 +550,55 @@ std::vector<std::string> parse_config_str_list(const StringRef &s,
// treated as a part of substring. // treated as a part of substring.
std::vector<StringRef> split_str(const StringRef &s, char delim); std::vector<StringRef> split_str(const StringRef &s, char delim);
// Returns given time |tp| in Common Log format (e.g., // Writes given time |tp| in Common Log format (e.g.,
// 03/Jul/2014:00:19:38 +0900) // 03/Jul/2014:00:19:38 +0900) in buffer pointed by |out|. The buffer
// Expected type of |tp| is std::chrono::timepoint // must be at least 27 bytes, including terminal NULL byte. Expected
template <typename T> std::string format_common_log(const T &tp) { // type of |tp| is std::chrono::time_point. This function returns
// StringRef wrapping the buffer pointed by |out|, and this string is
// terminated by NULL.
template <typename T> StringRef format_common_log(char *out, const T &tp) {
auto t = auto t =
std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()); std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
return common_log_date(t.count()); auto p = common_log_date(out, t.count());
*p = '\0';
return StringRef{out, p};
} }
// Returns given time |tp| in ISO 8601 format (e.g., // Returns given time |tp| in ISO 8601 format (e.g.,
// 2014-11-15T12:58:24.741Z) // 2014-11-15T12:58:24.741Z or 2014-11-15T12:58:24.741+09:00).
// Expected type of |tp| is std::chrono::timepoint // Expected type of |tp| is std::chrono::time_point
template <typename T> std::string format_iso8601(const T &tp) { template <typename T> std::string format_iso8601(const T &tp) {
auto t = std::chrono::duration_cast<std::chrono::milliseconds>( auto t = std::chrono::duration_cast<std::chrono::milliseconds>(
tp.time_since_epoch()); tp.time_since_epoch());
return iso8601_date(t.count()); return iso8601_date(t.count());
} }
// Returns given time |tp| in HTTP date format. // Writes given time |tp| in ISO 8601 format (e.g.,
template <typename T> std::string format_http_date(const T &tp) { // 2014-11-15T12:58:24.741Z or 2014-11-15T12:58:24.741+09:00) in
// buffer pointed by |out|. The buffer must be at least 30 bytes,
// including terminal NULL byte. Expected type of |tp| is
// std::chrono::time_point. This function returns StringRef wrapping
// the buffer pointed by |out|, and this string is terminated by NULL.
template <typename T> StringRef format_iso8601(char *out, const T &tp) {
auto t = std::chrono::duration_cast<std::chrono::milliseconds>(
tp.time_since_epoch());
auto p = iso8601_date(out, t.count());
*p = '\0';
return StringRef{out, p};
}
// Writes given time |tp| in HTTP Date format (e.g., Mon, 10 Oct 2016
// 10:25:58 GMT) in buffer pointed by |out|. The buffer must be at
// least 30 bytes, including terminal NULL byte. Expected type of
// |tp| is std::chrono::time_point. This function returns StringRef
// wrapping the buffer pointed by |out|, and this string is terminated
// by NULL.
template <typename T> StringRef format_http_date(char *out, const T &tp) {
auto t = auto t =
std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()); std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
return http_date(t.count()); auto p = http_date(out, t.count());
*p = '\0';
return StringRef{out, p};
} }
// Return the system precision of the template parameter |Clock| as // Return the system precision of the template parameter |Clock| as

View File

@ -194,6 +194,16 @@ void test_util_utox(void) {
void test_util_http_date(void) { void test_util_http_date(void) {
CU_ASSERT("Thu, 01 Jan 1970 00:00:00 GMT" == util::http_date(0)); CU_ASSERT("Thu, 01 Jan 1970 00:00:00 GMT" == util::http_date(0));
CU_ASSERT("Wed, 29 Feb 2012 09:15:16 GMT" == util::http_date(1330506916)); CU_ASSERT("Wed, 29 Feb 2012 09:15:16 GMT" == util::http_date(1330506916));
std::array<char, 30> http_buf;
CU_ASSERT("Thu, 01 Jan 1970 00:00:00 GMT" ==
util::format_http_date(http_buf.data(),
std::chrono::system_clock::time_point()));
CU_ASSERT("Wed, 29 Feb 2012 09:15:16 GMT" ==
util::format_http_date(http_buf.data(),
std::chrono::system_clock::time_point(
std::chrono::seconds(1330506916))));
} }
void test_util_select_h2(void) { void test_util_select_h2(void) {
@ -446,6 +456,21 @@ void test_util_localtime_date(void) {
CU_ASSERT_STRING_EQUAL("2001-10-02T00:34:56.123+12:00", CU_ASSERT_STRING_EQUAL("2001-10-02T00:34:56.123+12:00",
util::iso8601_date(1001939696000LL + 123).c_str()); util::iso8601_date(1001939696000LL + 123).c_str());
std::array<char, 27> common_buf;
CU_ASSERT("02/Oct/2001:00:34:56 +1200" ==
util::format_common_log(common_buf.data(),
std::chrono::system_clock::time_point(
std::chrono::seconds(1001939696))));
std::array<char, 30> iso8601_buf;
CU_ASSERT(
"2001-10-02T00:34:56.123+12:00" ==
util::format_iso8601(iso8601_buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL))));
if (tz) { if (tz) {
setenv("TZ", tz, 1); setenv("TZ", tz, 1);
} else { } else {