From db60ade81d43e9aaf03273d162878a9cc976f57b Mon Sep 17 00:00:00 2001 From: Vis Virial Date: Thu, 10 Nov 2016 21:58:15 +0900 Subject: [PATCH 1/2] http: do not use local settings for Date header --- evutil_time.c | 28 ++++++++++++++++++++++++++++ http.c | 15 +-------------- include/event2/util.h | 11 +++++++++++ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/evutil_time.c b/evutil_time.c index 32da2d6e..3a8424ea 100644 --- a/evutil_time.c +++ b/evutil_time.c @@ -145,6 +145,34 @@ evutil_usleep_(const struct timeval *tv) #endif } +int +evutil_date_rfc1123(char *date, const size_t datelen, struct tm *cur_p) { + static const char *DAYS[] = + { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const char *MONTHS[] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + time_t t = time(NULL); + + /* If `cur_p` is null, set system's current time. */ + if (cur_p == NULL) { +#ifdef _WIN32 + cur_p = gmtime(&t); +#else + { + struct tm cur; + gmtime_r(&t, &cur); + cur_p = &cur; + } +#endif + } + + return evutil_snprintf( + date, datelen, "%s, %02d %s %4d %02d:%02d:%02d GMT", + DAYS[cur_p->tm_wday], cur_p->tm_mday, MONTHS[cur_p->tm_mon], + 1900+cur_p->tm_year, cur_p->tm_hour, cur_p->tm_min, cur_p->tm_sec); +} + /* This function assumes it's called repeatedly with a not-actually-so-monotonic time source whose outputs are in 'tv'. It diff --git a/http.c b/http.c index 3c4116de..f5a2ef93 100644 --- a/http.c +++ b/http.c @@ -80,7 +80,6 @@ #include #endif #include -#include #ifdef EVENT__HAVE_UNISTD_H #include #endif @@ -491,19 +490,7 @@ evhttp_maybe_add_date_header(struct evkeyvalq *headers) { if (evhttp_find_header(headers, "Date") == NULL) { char date[50]; -#ifndef _WIN32 - struct tm cur; -#endif - struct tm *cur_p; - time_t t = time(NULL); -#ifdef _WIN32 - cur_p = gmtime(&t); -#else - gmtime_r(&t, &cur); - cur_p = &cur; -#endif - if (strftime(date, sizeof(date), - "%a, %d %b %Y %H:%M:%S GMT", cur_p) != 0) { + if (sizeof(date) - evutil_date_rfc1123(date, sizeof(date), NULL) > 0) { evhttp_add_header(headers, "Date", date); } } diff --git a/include/event2/util.h b/include/event2/util.h index 2c43e30e..defe00a3 100644 --- a/include/event2/util.h +++ b/include/event2/util.h @@ -77,6 +77,8 @@ extern "C" { #include #endif +#include + /* Some openbsd autoconf versions get the name of this macro wrong. */ #if defined(EVENT__SIZEOF_VOID__) && !defined(EVENT__SIZEOF_VOID_P) #define EVENT__SIZEOF_VOID_P EVENT__SIZEOF_VOID__ @@ -331,6 +333,15 @@ struct evutil_monotonic_timer #define EV_MONOT_PRECISE 1 #define EV_MONOT_FALLBACK 2 +/** Format a date string using RFC 1123 format (used in HTTP). + * If `cur_p` is NULL, current system's time will be used. + * The number of characters written will be returned. + * One should check if the return value is smaller than `datelen` to check if + * the result is truncated or not. + */ +EVENT2_EXPORT_SYMBOL +int evutil_date_rfc1123(char *date, const size_t datelen, struct tm *cur_p); + /** Allocate a new struct evutil_monotonic_timer for use with the * evutil_configure_monotonic_time() and evutil_gettime_monotonic() * functions. You must configure the timer with From 360aa233f08471d81ce6e11536f4fd561b51c438 Mon Sep 17 00:00:00 2001 From: Vis Virial Date: Fri, 16 Dec 2016 14:49:59 +0900 Subject: [PATCH 2/2] add tests for evutil_date_rfc1123(). --- test/regress_util.c | 65 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/regress_util.c b/test/regress_util.c index 60f085bf..0a57f7ce 100644 --- a/test/regress_util.c +++ b/test/regress_util.c @@ -1378,6 +1378,70 @@ end: ; } +static void +create_tm_from_unix_epoch(struct tm *cur_p, const time_t t) { +#ifdef _WIN32 + cur_p = gmtime(&t); +#else + gmtime_r(&t, cur_p); +#endif +} + +static struct date_rfc1123_case { + time_t t; + char date[30]; +} date_rfc1123_cases[] = { + { 0, "Thu, 01 Jan 1970 00:00:00 GMT"} /* UNIX time of zero */, + { 946684799, "Fri, 31 Dec 1999 23:59:59 GMT"} /* the last moment of the 20th century */, + { 946684800, "Sat, 01 Jan 2000 00:00:00 GMT"} /* the first moment of the 21st century */, + { 981072000, "Fri, 02 Feb 2001 00:00:00 GMT"}, + { 1015113600, "Sun, 03 Mar 2002 00:00:00 GMT"}, + { 1049414400, "Fri, 04 Apr 2003 00:00:00 GMT"}, + { 1083715200, "Wed, 05 May 2004 00:00:00 GMT"}, + { 1118016000, "Mon, 06 Jun 2005 00:00:00 GMT"}, + { 1152230400, "Fri, 07 Jul 2006 00:00:00 GMT"}, + { 1186531200, "Wed, 08 Aug 2007 00:00:00 GMT"}, + { 1220918400, "Tue, 09 Sep 2008 00:00:00 GMT"}, + { 1255132800, "Sat, 10 Oct 2009 00:00:00 GMT"}, + { 1289433600, "Thu, 11 Nov 2010 00:00:00 GMT"}, + { 1323648000, "Mon, 12 Dec 2011 00:00:00 GMT"}, + { 4294967296, "Sun, 07 Feb 2106 06:28:16 GMT"} /* 2^32 */, + {253402300799, "Fri, 31 Dec 9999 23:59:59 GMT"} /* long long future no one can imagine */, + { 1456704000, "Mon, 29 Feb 2016 00:00:00 GMT"} /* leap year */, + { 1435708800, "Wed, 01 Jul 2015 00:00:00 GMT"} /* leap second */, + { 1481866376, "Fri, 16 Dec 2016 05:32:56 GMT"} /* the time this test case is generated */, + {0, ""} /* end of test cases. */ +}; + +static void +test_evutil_date_rfc1123(void *arg) +{ + struct tm query; + char result[30]; + + /* Checks if too small buffers are safely accepted. */ + { + create_tm_from_unix_epoch(&query, 0); + evutil_date_rfc1123(result, 8, &query); + tt_str_op(result, ==, "Thu, 01"); + } + + /* Checks for testcases. */ + for (size_t i=0; ; i++) { + struct date_rfc1123_case c = date_rfc1123_cases[i]; + + if (strlen(c.date) == 0) + break; + + create_tm_from_unix_epoch(&query, c.t); + evutil_date_rfc1123(result, sizeof(result), &query); + tt_str_op(result, ==, c.date); + } + +end: + ; +} + struct testcase_t util_testcases[] = { { "ipv4_parse", regress_ipv4_parse, 0, NULL, NULL }, { "ipv6_parse", regress_ipv6_parse, 0, NULL, NULL }, @@ -1408,6 +1472,7 @@ struct testcase_t util_testcases[] = { { "monotonic_prc", test_evutil_monotonic_prc, 0, &basic_setup, (void*)"" }, { "monotonic_prc_precise", test_evutil_monotonic_prc, 0, &basic_setup, (void*)"precise" }, { "monotonic_prc_fallback", test_evutil_monotonic_prc, 0, &basic_setup, (void*)"fallback" }, + { "date_rfc1123", test_evutil_date_rfc1123, 0, NULL, NULL }, END_OF_TESTCASES, };