From 1a37044d3cefd17dbeadc5c69b63aad6c7e85872 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 10 Oct 2016 22:02:48 +0900 Subject: [PATCH] nghttpx: Use pre-allocated buffer for timestamp string --- src/shrpx_http2_upstream.cc | 2 +- src/shrpx_https_upstream.cc | 3 +- src/shrpx_log.cc | 6 +-- src/shrpx_log_config.cc | 6 +-- src/shrpx_log_config.h | 13 +++++-- src/shrpx_mruby_module_response.cc | 7 ++-- src/shrpx_spdy_upstream.cc | 4 +- src/util.cc | 56 +++++++++++++++------------ src/util.h | 62 ++++++++++++++++++++++++------ src/util_test.cc | 25 ++++++++++++ 10 files changed, 130 insertions(+), 54 deletions(-) diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index babd2602..e276532e 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -1488,7 +1488,7 @@ int Http2Upstream::error_reply(Downstream *downstream, auto response_status = http2::stringify_status(balloc, status_code); 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{ {http2::make_nv_ls_nocopy(":status", response_status), diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index db4aee92..4272608d 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -933,8 +933,7 @@ void HttpsUpstream::error_reply(unsigned int status_code) { output->append("\r\nDate: "); auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); - auto &date = lgconf->time_http_str; - output->append(date); + output->append(lgconf->time_http); output->append("\r\nContent-Type: text/html; " "charset=UTF-8\r\nConnection: close\r\n\r\n"); output->append(html); diff --git a/src/shrpx_log.cc b/src/shrpx_log.cc index d95d15cf..c0d4cbe1 100644 --- a/src/shrpx_log.cc +++ b/src/shrpx_log.cc @@ -138,7 +138,7 @@ Log::~Log() { auto tty = lgconf->errorlog_tty; lgconf->update_tstamp(std::chrono::system_clock::now()); - auto &time_local = lgconf->time_local_str; + auto &time_local = lgconf->time_local; if (severity_ == NOTICE) { rv = @@ -255,8 +255,8 @@ void upstream_accesslog(const std::vector &lfv, auto avail = sizeof(buf) - 2; lgconf->update_tstamp(lgsp.time_now); - auto &time_local = lgconf->time_local_str; - auto &time_iso8601 = lgconf->time_iso8601_str; + auto &time_local = lgconf->time_local; + auto &time_iso8601 = lgconf->time_iso8601; for (auto &lf : lfv) { switch (lf.type) { diff --git a/src/shrpx_log_config.cc b/src/shrpx_log_config.cc index 2222425c..d5d08170 100644 --- a/src/shrpx_log_config.cc +++ b/src/shrpx_log_config.cc @@ -62,9 +62,9 @@ void LogConfig::update_tstamp( time_str_updated_ = now; - time_local_str = util::format_common_log(now); - time_iso8601_str = util::format_iso8601(now); - time_http_str = util::format_http_date(now); + time_local = util::format_common_log(time_local_buf.data(), now); + time_iso8601 = util::format_iso8601(time_iso8601_buf.data(), now); + time_http = util::format_http_date(time_http_buf.data(), now); } } // namespace shrpx diff --git a/src/shrpx_log_config.h b/src/shrpx_log_config.h index b87adb4b..4e3bc5df 100644 --- a/src/shrpx_log_config.h +++ b/src/shrpx_log_config.h @@ -29,13 +29,20 @@ #include +#include "template.h" + +using namespace nghttp2; + namespace shrpx { struct LogConfig { std::chrono::system_clock::time_point time_str_updated_; - std::string time_local_str; - std::string time_iso8601_str; - std::string time_http_str; + std::array time_local_buf; + std::array time_iso8601_buf; + std::array time_http_buf; + StringRef time_local; + StringRef time_iso8601; + StringRef time_http; int accesslog_fd; int errorlog_fd; // true if errorlog_fd is referring to a terminal. diff --git a/src/shrpx_mruby_module_response.cc b/src/shrpx_mruby_module_response.cc index 8a89a058..da3dd45b 100644 --- a/src/shrpx_mruby_module_response.cc +++ b/src/shrpx_mruby_module_response.cc @@ -230,10 +230,9 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) { if (!date) { auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); - resp.fs.add_header_token( - StringRef::from_lit("date"), - make_string_ref(balloc, StringRef{lgconf->time_http_str}), false, - http2::HD_DATE); + resp.fs.add_header_token(StringRef::from_lit("date"), + make_string_ref(balloc, lgconf->time_http), false, + http2::HD_DATE); } auto upstream = downstream->get_upstream(); diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 2a0cfe1d..5f820680 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -1003,8 +1003,8 @@ int SpdyUpstream::error_reply(Downstream *downstream, const char *nv[] = {":status", status_string.c_str(), ":version", "http/1.1", "content-type", "text/html; charset=UTF-8", "server", get_config()->http.server_name.c_str(), "content-length", - content_length.c_str(), "date", - lgconf->time_http_str.c_str(), nullptr}; + content_length.c_str(), "date", lgconf->time_http.c_str(), + nullptr}; rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv, &data_prd); diff --git a/src/util.cc b/src/util.cc index 3e21628d..3153f34b 100644 --- a/src/util.cc +++ b/src/util.cc @@ -211,17 +211,20 @@ const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; } // namespace 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; - std::string res; if (gmtime_r(&t, &tms) == nullptr) { return res; } - /* Sat, 27 Sep 2014 06:31:15 GMT */ - res.resize(29); - - auto p = std::begin(res); + auto p = res; auto s = DAY_OF_WEEK[tms.tm_wday]; p = std::copy_n(s, 3, p); @@ -242,22 +245,24 @@ std::string http_date(time_t t) { s = " GMT"; p = std::copy_n(s, 4, p); - return res; + return p; } 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; if (localtime_r(&t, &tms) == nullptr) { - return ""; + return res; } - // Format data like this: - // 03/Jul/2014:00:19:38 +0900 - std::string res; - res.resize(26); - - auto p = std::begin(res); + auto p = res; p = cpydig(p, tms.tm_mday, 2); *p++ = '/'; @@ -288,24 +293,27 @@ std::string common_log_date(time_t t) { p = cpydig(p, gmtoff / 3600, 2); p = cpydig(p, (gmtoff % 3600) / 60, 2); - return res; + return p; } 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; tm tms; if (localtime_r(&sec, &tms) == nullptr) { - return ""; + return res; } - // Format data like this: - // 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); + auto p = res; p = cpydig(p, tms.tm_year + 1900, 4); *p++ = '-'; @@ -340,9 +348,7 @@ std::string iso8601_date(int64_t ms) { p = cpydig(p, (gmtoff % 3600) / 60, 2); } - res.resize(p - std::begin(res)); - - return res; + return p; } time_t parse_http_date(const StringRef &s) { diff --git a/src/util.h b/src/util.h index 38ae7c8d..4659dbd9 100644 --- a/src/util.h +++ b/src/util.h @@ -147,15 +147,29 @@ template std::string format_hex(const std::array &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); +// 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., // 03/Jul/2014:00:19:38 +0900) 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., -// 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); +// 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); @@ -536,29 +550,55 @@ std::vector parse_config_str_list(const StringRef &s, // treated as a part of substring. std::vector split_str(const StringRef &s, char delim); -// Returns given time |tp| in Common Log format (e.g., -// 03/Jul/2014:00:19:38 +0900) -// Expected type of |tp| is std::chrono::timepoint -template std::string format_common_log(const T &tp) { +// Writes given time |tp| in Common Log format (e.g., +// 03/Jul/2014:00:19:38 +0900) in buffer pointed by |out|. The buffer +// must be at least 27 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 StringRef format_common_log(char *out, const T &tp) { auto t = std::chrono::duration_cast(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., -// 2014-11-15T12:58:24.741Z) -// Expected type of |tp| is std::chrono::timepoint +// 2014-11-15T12:58:24.741Z or 2014-11-15T12:58:24.741+09:00). +// Expected type of |tp| is std::chrono::time_point template std::string format_iso8601(const T &tp) { auto t = std::chrono::duration_cast( tp.time_since_epoch()); return iso8601_date(t.count()); } -// Returns given time |tp| in HTTP date format. -template std::string format_http_date(const T &tp) { +// Writes given time |tp| in ISO 8601 format (e.g., +// 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 StringRef format_iso8601(char *out, const T &tp) { + auto t = std::chrono::duration_cast( + 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 StringRef format_http_date(char *out, const T &tp) { auto t = std::chrono::duration_cast(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 diff --git a/src/util_test.cc b/src/util_test.cc index c8934402..e7da8f03 100644 --- a/src/util_test.cc +++ b/src/util_test.cc @@ -194,6 +194,16 @@ void test_util_utox(void) { void test_util_http_date(void) { 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)); + + std::array 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) { @@ -446,6 +456,21 @@ void test_util_localtime_date(void) { CU_ASSERT_STRING_EQUAL("2001-10-02T00:34:56.123+12:00", util::iso8601_date(1001939696000LL + 123).c_str()); + std::array 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 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) { setenv("TZ", tz, 1); } else {