From 6a39de0ae51e3d9d3f075596ffdc113c579c50c3 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 28 Jan 2015 00:36:44 +0900 Subject: [PATCH] nghttpx: Accept s or ms as unit for argument --- src/shrpx-unittest.cc | 2 + src/shrpx.cc | 59 ++++++++++++++++++----------- src/shrpx_config.cc | 41 +++++++++----------- src/util.cc | 88 ++++++++++++++++++++++++++++++++----------- src/util.h | 14 +++++++ src/util_test.cc | 20 ++++++++++ src/util_test.h | 1 + 7 files changed, 155 insertions(+), 70 deletions(-) diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc index 59c900f3..900e8f4a 100644 --- a/src/shrpx-unittest.cc +++ b/src/shrpx-unittest.cc @@ -134,6 +134,8 @@ int main(int argc, char *argv[]) { !CU_add_test(pSuite, "util_parse_uint_with_unit", shrpx::test_util_parse_uint_with_unit) || !CU_add_test(pSuite, "util_parse_uint", shrpx::test_util_parse_uint) || + !CU_add_test(pSuite, "util_parse_time_with_unit", + shrpx::test_util_parse_time_with_unit) || !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) || !CU_add_test(pSuite, "ringbuf_write", nghttp2::test_ringbuf_write) || !CU_add_test(pSuite, "ringbuf_iovec", nghttp2::test_ringbuf_iovec) || diff --git a/src/shrpx.cc b/src/shrpx.cc index 6134fc26..27bf99e1 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -902,46 +902,55 @@ Performance: --num-accept= The number of connections acceptor can accept at once. Default: )" << get_config()->num_accept << R"( - --accept-delay= - Acceptors get idle in milliseconds after they + --accept-delay= + Acceptors get idle in amount of time after they accepted at most N connections, where N is defined in --num-accept option. - Default: )" << static_cast(get_config()->accept_delay * 1000) + Default: )" << util::duration_str(get_config()->accept_delay) << R"( Timeout: - --frontend-http2-read-timeout= + --frontend-http2-read-timeout= Specify read timeout for HTTP/2 and SPDY frontend connection. - Default: )" << get_config()->http2_upstream_read_timeout << R"( - --frontend-read-timeout= + Default: )" + << util::duration_str(get_config()->http2_upstream_read_timeout) << R"( + --frontend-read-timeout= Specify read timeout for HTTP/1.1 frontend connection. - Default: )" << get_config()->upstream_read_timeout << R"( - --frontend-write-timeout= + Default: )" + << util::duration_str(get_config()->upstream_read_timeout) << R"( + --frontend-write-timeout= Specify write timeout for all frontend connections. - Default: )" << get_config()->upstream_write_timeout << R"( - --stream-read-timeout= + Default: )" + << util::duration_str(get_config()->upstream_write_timeout) << R"( + --stream-read-timeout= Specify read timeout for HTTP/2 and SPDY streams. 0 means no timeout. - Default: )" << get_config()->stream_read_timeout << R"( - --stream-write-timeout= + Default: )" + << util::duration_str(get_config()->stream_read_timeout) << R"( + --stream-write-timeout= Specify write timeout for HTTP/2 and SPDY streams. 0 means no timeout. - Default: )" << get_config()->stream_write_timeout << R"( - --backend-read-timeout= + Default: )" + << util::duration_str(get_config()->stream_write_timeout) << R"( + --backend-read-timeout= Specify read timeout for backend connection. - Default: )" << get_config()->downstream_read_timeout << R"( - --backend-write-timeout= + Default: )" + << util::duration_str(get_config()->downstream_read_timeout) << R"( + --backend-write-timeout= Specify write timeout for backend connection. - Default: )" << get_config()->downstream_write_timeout << R"( - --backend-keep-alive-timeout= + Default: )" + << util::duration_str(get_config()->downstream_write_timeout) << R"( + --backend-keep-alive-timeout= Specify keep-alive timeout for backend connection. - Default: )" << get_config()->downstream_idle_read_timeout << R"( - --listener-disable-timeout= + Default: )" + << util::duration_str(get_config()->downstream_idle_read_timeout) << R"( + --listener-disable-timeout= After accepting connection failed, connection listener - is disabled for a given time in seconds. Specifying 0 + is disabled for a given amount of time. Specifying 0 disables this feature. - Default: )" << get_config()->listener_disable_timeout << R"( + Default: )" + << util::duration_str(get_config()->listener_disable_timeout) << R"( SSL/TLS: --ciphers= @@ -1203,7 +1212,11 @@ Misc: -h, --help Print this help and exit. The argument is an integer and an optional unit (e.g., 10K is - 10 * 1024). Units are K, M and G (powers of 1024).)" << std::endl; + 10 * 1024). Units are K, M and G (powers of 1024). + + The argument is an integer and an optional unit (e.g., 1s is 1 + second and 500ms is 500 milliseconds). Units are s or ms. If a + unit is omitted, a second is used as unit.)" << std::endl; } } // namespace diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 13fc354d..f3057e67 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -503,14 +503,14 @@ std::vector parse_log_format(const char *optarg) { } namespace { -int parse_timeval(ev_tstamp *dest, const char *opt, const char *optarg) { - time_t sec; - - if (parse_uint(&sec, opt, optarg) != 0) { +int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) { + auto t = util::parse_time_with_unit(optarg); + if (t == std::numeric_limits::infinity()) { + LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; return -1; } - *dest = sec; + *dest = t; return 0; } @@ -604,32 +604,32 @@ int parse_config(const char *opt, const char *optarg) { } if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT)) { - return parse_timeval(&mod_config()->http2_upstream_read_timeout, opt, - optarg); + return parse_duration(&mod_config()->http2_upstream_read_timeout, opt, + optarg); } if (util::strieq(opt, SHRPX_OPT_FRONTEND_READ_TIMEOUT)) { - return parse_timeval(&mod_config()->upstream_read_timeout, opt, optarg); + return parse_duration(&mod_config()->upstream_read_timeout, opt, optarg); } if (util::strieq(opt, SHRPX_OPT_FRONTEND_WRITE_TIMEOUT)) { - return parse_timeval(&mod_config()->upstream_write_timeout, opt, optarg); + return parse_duration(&mod_config()->upstream_write_timeout, opt, optarg); } if (util::strieq(opt, SHRPX_OPT_BACKEND_READ_TIMEOUT)) { - return parse_timeval(&mod_config()->downstream_read_timeout, opt, optarg); + return parse_duration(&mod_config()->downstream_read_timeout, opt, optarg); } if (util::strieq(opt, SHRPX_OPT_BACKEND_WRITE_TIMEOUT)) { - return parse_timeval(&mod_config()->downstream_write_timeout, opt, optarg); + return parse_duration(&mod_config()->downstream_write_timeout, opt, optarg); } if (util::strieq(opt, SHRPX_OPT_STREAM_READ_TIMEOUT)) { - return parse_timeval(&mod_config()->stream_read_timeout, opt, optarg); + return parse_duration(&mod_config()->stream_read_timeout, opt, optarg); } if (util::strieq(opt, SHRPX_OPT_STREAM_WRITE_TIMEOUT)) { - return parse_timeval(&mod_config()->stream_write_timeout, opt, optarg); + return parse_duration(&mod_config()->stream_write_timeout, opt, optarg); } if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FILE)) { @@ -663,8 +663,8 @@ int parse_config(const char *opt, const char *optarg) { } if (util::strieq(opt, SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT)) { - return parse_timeval(&mod_config()->downstream_idle_read_timeout, opt, - optarg); + return parse_duration(&mod_config()->downstream_idle_read_timeout, opt, + optarg); } if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS) || @@ -1104,7 +1104,7 @@ int parse_config(const char *opt, const char *optarg) { } if (util::strieq(opt, SHRPX_OPT_LISTENER_DISABLE_TIMEOUT)) { - return parse_timeval(&mod_config()->listener_disable_timeout, opt, optarg); + return parse_duration(&mod_config()->listener_disable_timeout, opt, optarg); } if (util::strieq(opt, SHRPX_OPT_TLS_TICKET_KEY_FILE)) { @@ -1170,14 +1170,7 @@ int parse_config(const char *opt, const char *optarg) { } if (util::strieq(opt, SHRPX_OPT_ACCEPT_DELAY)) { - size_t n; - if (parse_uint(&n, opt, optarg) != 0) { - return -1; - } - - mod_config()->accept_delay = n / 1000.; - - return 0; + return parse_duration(&mod_config()->accept_delay, opt, optarg); } if (util::strieq(opt, "conf")) { diff --git a/src/util.cc b/src/util.cc index eafe13b9..f1b0cd9f 100644 --- a/src/util.cc +++ b/src/util.cc @@ -899,32 +899,48 @@ bool ipv6_numeric_addr(const char *host) { return inet_pton(AF_INET6, host, dst) == 1; } -int64_t parse_uint_with_unit(const char *s) { +namespace { +std::pair parse_uint_digits(const void *ss, size_t len) { + const uint8_t *s = static_cast(ss); int64_t n = 0; size_t i; - auto len = strlen(s); if (len == 0) { - return -1; + return {-1, 0}; } constexpr int64_t max = std::numeric_limits::max(); for (i = 0; i < len; ++i) { if ('0' <= s[i] && s[i] <= '9') { if (n > max / 10) { - return -1; + return {-1, 0}; } n *= 10; if (n > max - (s[i] - '0')) { - return -1; + return {-1, 0}; } n += s[i] - '0'; continue; } break; } + if (i == 0) { + return {-1, 0}; + } + return {n, i}; +} +} // namespace + +int64_t parse_uint_with_unit(const char *s) { + int64_t n; + size_t i; + auto len = strlen(s); + std::tie(n, i) = parse_uint_digits(s, len); + if (n == -1) { + return -1; + } if (i == len) { return n; } - if (i == 0 || i + 1 != len) { + if (i + 1 != len) { return -1; } int mul = 1; @@ -944,6 +960,7 @@ int64_t parse_uint_with_unit(const char *s) { default: return -1; } + constexpr int64_t max = std::numeric_limits::max(); if (n > max / mul) { return -1; } @@ -959,28 +976,53 @@ int64_t parse_uint(const std::string &s) { } int64_t parse_uint(const uint8_t *s, size_t len) { - if (len == 0) { - return -1; - } - constexpr int64_t max = std::numeric_limits::max(); - int64_t n = 0; - for (size_t i = 0; i < len; ++i) { - if ('0' <= s[i] && s[i] <= '9') { - if (n > max / 10) { - return -1; - } - n *= 10; - if (n > max - (s[i] - '0')) { - return -1; - } - n += s[i] - '0'; - continue; - } + int64_t n; + size_t i; + std::tie(n, i) = parse_uint_digits(s, len); + if (n == -1 || i != len) { return -1; } return n; } +double parse_time_with_unit(const char *s) { + int64_t n; + size_t i; + auto len = strlen(s); + std::tie(n, i) = parse_uint_digits(s, len); + if (n == -1) { + goto fail; + } + if (i == len) { + return static_cast(n); + } + switch (s[i]) { + case 'S': + case 's': + if (i + 1 != len) { + goto fail; + } + return static_cast(n); + break; + case 'M': + case 'm': + if (i + 2 != len || (s[i + 1] != 's' && s[i + 1] != 'S')) { + goto fail; + } + return static_cast(n) / 1000.; + } +fail: + return std::numeric_limits::infinity(); +} + +std::string duration_str(double t) { + auto frac = static_cast(t * 1000) % 1000; + if (frac > 0) { + return utos(static_cast(t * 1000)) + "ms"; + } + return utos(static_cast(t)) + "s"; +} + } // namespace util } // namespace nghttp2 diff --git a/src/util.h b/src/util.h index 8f63fe37..059fb0d3 100644 --- a/src/util.h +++ b/src/util.h @@ -508,6 +508,20 @@ int64_t parse_uint(const char *s); int64_t parse_uint(const uint8_t *s, size_t len); int64_t parse_uint(const std::string &s); +// Parses NULL terminated string |s| as unsigned integer and returns +// the parsed integer casted to double. If |s| ends with "s", the +// parsed value's unit is a second. If |s| ends with "ms", the unit +// is millisecond. If none of them are given, the unit is second. +// This function returns std::numeric_limits::infinity() if +// error occurs. +double parse_time_with_unit(const char *s); + +// Returns string representation of time duration |t|. If t has +// fractional part (at least more than or equal to 1e-3), |t| is +// multiplied by 1000 and the unit "ms" is appended. Otherwise, |t| +// is left as is and "s" is appended. +std::string duration_str(double t); + } // namespace util } // namespace nghttp2 diff --git a/src/util_test.cc b/src/util_test.cc index a61fba3b..c6a47e50 100644 --- a/src/util_test.cc +++ b/src/util_test.cc @@ -223,4 +223,24 @@ void test_util_parse_uint(void) { CU_ASSERT(-1 == util::parse_uint("")); } +void test_util_parse_time_with_unit(void) { + CU_ASSERT(0. == util::parse_time_with_unit("0")); + CU_ASSERT(123. == util::parse_time_with_unit("123")); + CU_ASSERT(123. == util::parse_time_with_unit("123s")); + CU_ASSERT(0.500 == util::parse_time_with_unit("500ms")); + CU_ASSERT(123. == util::parse_time_with_unit("123S")); + CU_ASSERT(0.500 == util::parse_time_with_unit("500MS")); + + auto err = std::numeric_limits::infinity(); + // check overflow case + CU_ASSERT(err == util::parse_time_with_unit("9223372036854775808")); + // bad characters + CU_ASSERT(err == util::parse_time_with_unit("0u")); + CU_ASSERT(err == util::parse_time_with_unit("0xs")); + CU_ASSERT(err == util::parse_time_with_unit("0mt")); + CU_ASSERT(err == util::parse_time_with_unit("0mss")); + CU_ASSERT(err == util::parse_time_with_unit("s")); + CU_ASSERT(err == util::parse_time_with_unit("ms")); +} + } // namespace shrpx diff --git a/src/util_test.h b/src/util_test.h index 7f6acb1d..b741ab2a 100644 --- a/src/util_test.h +++ b/src/util_test.h @@ -40,6 +40,7 @@ void test_util_ipv6_numeric_addr(void); void test_util_utos_with_unit(void); void test_util_parse_uint_with_unit(void); void test_util_parse_uint(void); +void test_util_parse_time_with_unit(void); } // namespace shrpx