Browse Source
http_date.[ch] encapsulate HTTP-date parse/compare (import from one of my development branches from 2015)master
9 changed files with 159 additions and 1 deletions
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 */ |
||||
} |
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* http_date - HTTP date manipulation |
||||
* |
||||
* Copyright(c) 2015 Glenn Strauss gstrauss()gluelogic.com All rights reserved |
||||
* License: BSD 3-clause (same as lighttpd) |
||||
*/ |
||||
#ifndef INCLUDED_HTTP_DATE_H |
||||
#define INCLUDED_HTTP_DATE_H |
||||
#include "first.h" |
||||
|
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
|
||||
#define HTTP_DATE_SZ 30 /* (IMF-fixdate is 29 chars + '\0') */ |
||||
|
||||
size_t http_date_time_to_str (char *s, size_t max, time_t t); |
||||
|
||||
int http_date_if_modified_since (const char *ifmod, const char *lmod, time_t lmtime); |
||||
|
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
|
||||
#endif |
Loading…
Reference in new issue