You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
4.1 KiB
121 lines
4.1 KiB
2 years ago
|
/*
|
||
|
* http_date - HTTP date manipulation
|
||
|
*
|
||
|
* Copyright(c) 2015 Glenn Strauss gstrauss()gluelogic.com All rights reserved
|
||
|
* License: BSD 3-clause (same as lighttpd)
|
||
|
*/
|
||
|
#include "http_date.h"
|
||
|
|
||
|
#include "sys-time.h"
|
||
|
|
||
|
/**
|
||
|
* https://tools.ietf.org/html/rfc7231
|
||
|
* [RFC7231] 7.1.1.1 Date/Time Formats
|
||
|
* Prior to 1995, there were three different formats commonly used by
|
||
|
* servers to communicate timestamps. For compatibility with old
|
||
|
* implementations, all three are defined here. The preferred format is
|
||
|
* a fixed-length and single-zone subset of the date and time
|
||
|
* specification used by the Internet Message Format [RFC5322].
|
||
|
* HTTP-date = IMF-fixdate / obs-date
|
||
|
* An example of the preferred format is
|
||
|
* Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate
|
||
|
*
|
||
|
*
|
||
|
* (intended for use with strftime() and strptime())
|
||
|
* "%a, %d %b %Y %T GMT"
|
||
|
*/
|
||
|
|
||
|
|
||
|
#if defined(__CYGWIN__) && defined(__STRICT_ANSI__)
|
||
|
/* (prototype for strptime() from cygwin /usr/include/time.h) */
|
||
|
char *_EXFUN(strptime, (const char *__restrict,
|
||
|
const char *__restrict,
|
||
|
struct tm *__restrict));
|
||
|
#endif
|
||
|
|
||
|
|
||
|
static char *
|
||
|
http_date_str_to_tm (const char * const s, struct tm * const tm)
|
||
|
{
|
||
|
/* attempt strptime() using multiple date formats
|
||
|
* support RFC 822,1123; RFC 850; and ANSI C asctime() date strings,
|
||
|
* as required by [RFC7231] https://tools.ietf.org/html/rfc7231#section-7.1
|
||
|
* [RFC7231] 7.1.1.1 Date/Time Formats
|
||
|
* HTTP-date = IMF-fixdate / obs-date
|
||
|
* [...]
|
||
|
* A recipient that parses a timestamp value in an HTTP header field
|
||
|
* MUST accept all three HTTP-date formats.
|
||
|
*/
|
||
|
|
||
|
static const char *datefmts[] = {
|
||
|
"%a, %d %b %Y %T GMT", /* RFC 822, RFC 1123; RFC 7231 IMF-fixdate */
|
||
|
"%A, %d-%b-%y %T GMT", /* RFC 850 */
|
||
|
"%a %b %d %T %Y" /* ANSI C asctime() (obsolete in POSIX.1-2008) */
|
||
|
};
|
||
|
|
||
|
char *p;
|
||
|
int i = 0;
|
||
|
do {
|
||
|
p = strptime(s, datefmts[i], tm);
|
||
|
} while (__builtin_expect( (NULL == p), 0)
|
||
|
&& ++i < (int)(sizeof(datefmts)/sizeof(char *)));
|
||
|
return p; /* NULL if error; date string could not be parsed */
|
||
|
}
|
||
|
|
||
|
|
||
|
size_t
|
||
|
http_date_time_to_str (char * const s, const size_t max, const time_t t)
|
||
|
{
|
||
|
/*('max' is expected to be >= 30 (IMF-fixdate is 29 chars + '\0'))*/
|
||
|
struct tm tm;
|
||
|
return (__builtin_expect( (NULL != gmtime_r(&t, &tm)), 1))
|
||
|
? strftime(s, max, "%a, %d %b %Y %T GMT", &tm) /* IMF-fixdate format */
|
||
|
: 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef HAVE_TIMEGM
|
||
|
#define http_date_timegm(tm) timegm(tm)
|
||
|
#else
|
||
|
#ifdef _WIN32
|
||
|
#define http_date_timegm(tm) _mkgmtime(tm)
|
||
|
#else
|
||
|
/* If OS missing timegm(), then for best portability it is recommended to set
|
||
|
* $ export LC_TIME=C TZ=UTC0
|
||
|
*
|
||
|
* tm->tm_isdst = 0 for mktime() to indicate daylight saving time not in effect
|
||
|
* which is fine since two strings should be GMT dates, and both are converted
|
||
|
* with mktime() and then the results compared */
|
||
|
#define http_date_timegm(tm) ((tm)->tm_isdst = 0, mktime(tm))
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
|
||
|
int
|
||
|
http_date_if_modified_since (const char * const ifmod,
|
||
|
const char * const lmod, time_t lmtime)
|
||
|
{
|
||
|
/* if caller provides non-zero lmtime, it must match Last-Modified lmod,
|
||
|
* and will typically be same arg given to http_response_set_last_modified()
|
||
|
* (small opt to elide one strptime(),timegm() call)
|
||
|
* (In absense of timegm(), substitute mktime(), which works reasonably well
|
||
|
* since mktime() strings are used for comparison of If-Modified-Since)
|
||
|
* (use mktime() to convert both strings for compare) */
|
||
|
struct tm ifmodtm;
|
||
|
if (NULL == http_date_str_to_tm(ifmod, &ifmodtm))
|
||
|
return 1; /* date parse error */
|
||
|
#if defined(HAVE_TIMEGM) || defined(_WIN32)
|
||
|
if (0 == lmtime)
|
||
|
#endif
|
||
|
{
|
||
|
struct tm lmodtm;
|
||
|
if (NULL == http_date_str_to_tm(lmod, &lmodtm))
|
||
|
return 1; /* date parse error */
|
||
|
lmtime = http_date_timegm(&lmodtm);
|
||
|
}
|
||
|
const time_t ifmtime = http_date_timegm(&ifmodtm);
|
||
|
return (lmtime > ifmtime);
|
||
|
/* returns 0 if not modified since,
|
||
|
* returns 1 if modified since or date parse error */
|
||
|
}
|