Browse Source

[multiple] employ http_date.h, sys-time.h

- replace use of strptime() w/ implementation specialized for HTTP dates
- use thread-safe gmtime_r(), localtime_r() (replace localtime, gmtime)
master
Glenn Strauss 7 months ago
parent
commit
122094e3e3
  1. 2
      src/buffer.c
  2. 10
      src/h2.c
  3. 75
      src/http-header-glue.c
  4. 47
      src/http_date.c
  5. 4
      src/http_date.h
  6. 19
      src/log.c
  7. 19
      src/mod_accesslog.c
  8. 8
      src/mod_cml_lua.c
  9. 23
      src/mod_dirlisting.c
  10. 5
      src/mod_expire.c
  11. 61
      src/mod_ssi.c
  12. 6
      src/mod_status.c
  13. 21
      src/mod_webdav.c
  14. 19
      src/response.c
  15. 4
      src/response.h
  16. 8
      src/sys-time.h
  17. 10
      tests/cachable.t

2
src/buffer.c

@ -261,7 +261,7 @@ void buffer_append_strftime(buffer * const restrict b, const char * const restri
size_t rv;
char* buf;
force_assert(NULL != format);
force_assert(NULL != tm);
if (NULL == tm) return;
buf = buffer_string_prepare_append(b, 255);
rv = strftime(buf, buffer_string_space(b), format, tm);

10
src/h2.c

@ -16,6 +16,7 @@
#include "buffer.h"
#include "chunk.h"
#include "fdevent.h" /* FDEVENT_STREAM_REQUEST_BUFMIN */
#include "http_date.h"
#include "http_header.h"
#include "log.h"
#include "request.h"
@ -1912,6 +1913,7 @@ h2_send_headers (request_st * const r, connection * const con)
if (!light_btst(r->resp_htags, HTTP_HEADER_DATE)) {
/* HTTP/1.1 and later requires a Date: header */
/* "date: " 6-chars + 30-chars for "%a, %d %b %Y %H:%M:%S GMT" + '\0' */
static time_t tlast = 0;
static char tstr[36] = "date: ";
memset(&lsx, 0, sizeof(lsxpack_header_t));
@ -1923,13 +1925,9 @@ h2_send_headers (request_st * const r, connection * const con)
lsx.hpack_index = LSHPACK_HDR_DATE;
/* cache the generated timestamp */
static time_t tlast;
const time_t cur_ts = log_epoch_secs;
if (tlast != cur_ts) {
tlast = cur_ts;
strftime(tstr+6, sizeof(tstr)-6,
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&tlast));
}
if (__builtin_expect ( (tlast != cur_ts), 0))
http_date_time_to_str(tstr+6, sizeof(tstr)-6, (tlast = cur_ts));
alen += 35+2;

75
src/http-header-glue.c

@ -1,5 +1,7 @@
#include "first.h"
#include "sys-time.h"
#include "base.h"
#include "array.h"
#include "buffer.h"
@ -8,6 +10,7 @@
#include "log.h"
#include "etag.h"
#include "http_chunk.h"
#include "http_date.h"
#include "http_header.h"
#include "response.h"
#include "sock_addr.h"
@ -18,8 +21,6 @@
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include "sys-socket.h"
#include <unistd.h>
@ -150,7 +151,7 @@ void strftime_cache_reset(void) {
}
}
const buffer * strftime_cache_get(const time_t last_mod) {
static const buffer * strftime_cache_get(const time_t last_mod) {
static int mtime_cache_idx;
for (int j = 0; j < MTIME_CACHE_MAX; ++j) {
@ -161,15 +162,28 @@ const buffer * strftime_cache_get(const time_t last_mod) {
if (++mtime_cache_idx == MTIME_CACHE_MAX) mtime_cache_idx = 0;
const int i = mtime_cache_idx;
mtime_cache[i].mtime = last_mod;
strftime(mtime_cache[i].str.ptr, sizeof(mtime_cache_str[0]),
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime_cache[i].mtime));
http_date_time_to_str(mtime_cache[i].str.ptr, sizeof(mtime_cache_str[0]),
(mtime_cache[i].mtime = last_mod));
return &mtime_cache[i].str;
}
int http_response_handle_cachable(request_st * const r, const buffer * const mtime) {
const buffer * http_response_set_last_modified(request_st * const r, const time_t lmtime) {
const buffer * const mtime = strftime_cache_get(lmtime);
http_header_response_set(r, HTTP_HEADER_LAST_MODIFIED,
CONST_STR_LEN("Last-Modified"),
CONST_BUF_LEN(mtime));
#if 0
return http_header_response_get(r, HTTP_HEADER_LAST_MODIFIED,
CONST_STR_LEN("Last-Modified"));
#else
return mtime;
#endif
}
int http_response_handle_cachable(request_st * const r, const buffer * const lmod, const time_t lmtime) {
const buffer *vb;
/*
@ -200,44 +214,8 @@ int http_response_handle_cachable(request_st * const r, const buffer * const mti
&& (vb = http_header_request_get(r, HTTP_HEADER_IF_MODIFIED_SINCE,
CONST_STR_LEN("If-Modified-Since")))) {
/* last-modified handling */
size_t used_len;
char *semicolon;
if (NULL == (semicolon = strchr(vb->ptr, ';'))) {
used_len = buffer_string_length(vb);
} else {
used_len = semicolon - vb->ptr;
}
if (buffer_is_equal_string(mtime, vb->ptr, used_len)) {
if ('\0' == mtime->ptr[used_len]) r->http_status = 304;
return HANDLER_FINISHED;
} else {
char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
time_t t_header, t_file;
struct tm tm;
/* convert to timestamp */
if (used_len >= sizeof(buf)) return HANDLER_GO_ON;
memcpy(buf, vb->ptr, used_len);
buf[used_len] = '\0';
if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) {
/**
* parsing failed, let's get out of here
*/
return HANDLER_GO_ON;
}
tm.tm_isdst = 0;
t_header = mktime(&tm);
strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm);
tm.tm_isdst = 0;
t_file = mktime(&tm);
if (t_file > t_header) return HANDLER_GO_ON;
if (buffer_is_equal(lmod, vb)
|| !http_date_if_modified_since(CONST_BUF_LEN(vb), lmtime)) {
r->http_status = 304;
return HANDLER_FINISHED;
}
@ -669,13 +647,10 @@ void http_response_send_file (request_st * const r, buffer * const path) {
mtime = http_header_response_get(r, HTTP_HEADER_LAST_MODIFIED,
CONST_STR_LEN("Last-Modified"));
if (NULL == mtime) {
mtime = strftime_cache_get(sce->st.st_mtime);
http_header_response_set(r, HTTP_HEADER_LAST_MODIFIED,
CONST_STR_LEN("Last-Modified"),
CONST_BUF_LEN(mtime));
mtime = http_response_set_last_modified(r, sce->st.st_mtime);
}
if (HANDLER_FINISHED == http_response_handle_cachable(r, mtime)) {
if (HANDLER_FINISHED == http_response_handle_cachable(r, mtime, sce->st.st_mtime)) {
return;
}
}

47
src/http_date.c

@ -234,8 +234,10 @@ http_date_parse_IMF_fixdate (const char * const s, struct tm * const tm)
static const char *
http_date_str_to_tm (const char * const s, struct tm * const tm)
http_date_str_to_tm (const char * const s, const uint32_t len,
struct tm * const tm)
{
/* attempt strptime() using multiple date formats
* support RFC 822,1123,7231; RFC 850; and ANSI C asctime() date strings,
* as required by [RFC7231] https://tools.ietf.org/html/rfc7231#section-7.1
@ -250,8 +252,9 @@ http_date_str_to_tm (const char * const s, struct tm * const tm)
* - HTTP expected date formats are known, so not needed as input param
* - HTTP expected date string content is in C locale and is case-sensitive
* - returns (const char *) instead of strptime() (char *) return type
* - returns NULL if error (if date string could not be parsed) */
const size_t len = strlen(s); /*(require '\0'-terminated string)*/
* - returns NULL if error (if date string could not be parsed)
* Note: internal implementation requires '\0'-terminated string, or at
* least one valid char after partial match of RFC 850 or asctime formats */
if (len == 29)
return http_date_parse_IMF_fixdate(s, tm);
else if (len > 29)
@ -261,13 +264,14 @@ http_date_str_to_tm (const char * const s, struct tm * const tm)
}
size_t
http_date_time_to_str (char * const s, const size_t max, const time_t t)
uint32_t
http_date_time_to_str (char * const s, const size_t sz, const time_t t)
{
/*('max' is expected to be >= 30 (IMF-fixdate is 29 chars + '\0'))*/
struct tm tm;
const char fmt[] = "%a, %d %b %Y %T GMT"; /*IMF-fixdate fmt*/
return (__builtin_expect( (NULL != gmtime_r(&t, &tm)), 1))
? strftime(s, max, "%a, %d %b %Y %T GMT", &tm) /* IMF-fixdate format */
? (uint32_t)strftime(s, sz, fmt, &tm)
: 0;
}
@ -280,37 +284,28 @@ http_date_time_to_str (char * const s, const size_t max, const time_t t)
#else
/* If OS missing timegm(), then for best portability it is recommended to set
* $ export LC_TIME=C TZ=UTC0
* or else time comparisons may be off by timezone offset (!!)
*
* 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 */
* which is fine since HTTP date strings should be GMT dates
*
* If not TZ=UTC0, and timegm() is not present, then can we expect gmtime() to
* be available so that we can mktime(gmtime_r(lmtime, &gtm)) ? That would be
* an expensive way to obtain TZ offset. XXX: maybe calculate TZ offset once
* at startup? Are there any (modern) systems missing timegm() or _mkgmtime()?
*/
#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)
http_date_if_modified_since (const char * const ifmod, const uint32_t ifmodlen,
const 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))
if (NULL == http_date_str_to_tm(ifmod, ifmodlen, &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,

4
src/http_date.h

@ -16,9 +16,9 @@ extern "C" {
#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);
uint32_t http_date_time_to_str (char *s, size_t sz, time_t t);
int http_date_if_modified_since (const char *ifmod, const char *lmod, time_t lmtime);
int http_date_if_modified_since (const char *ifmod, uint32_t ifmodlen, time_t lmtime);
#ifdef __cplusplus

19
src/log.c

@ -8,8 +8,8 @@
#include "log.h"
#include <sys/types.h>
#include "sys-time.h"
#include <errno.h>
#include <time.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h> /* vsnprintf() */
@ -60,22 +60,27 @@ ssize_t write_all(int fd, const void * const buf, size_t count) {
static int log_buffer_prepare(const log_error_st *errh, const char *filename, unsigned int line, buffer *b) {
static time_t tlast;
static char tstr[20]; /* 20-chars needed for "%Y-%m-%d %H:%M:%S" */
static size_t tlen;
static uint32_t tlen;
static char tstr[24]; /* 20 "%Y-%m-%d %H:%M:%S" incl '\0' +3 ": (" */
switch(errh->errorlog_mode) {
case ERRORLOG_PIPE:
case ERRORLOG_FILE:
case ERRORLOG_FD:
if (-1 == errh->errorlog_fd) return -1;
/* cache the generated timestamp */
if (tlast != log_epoch_secs) {
if (__builtin_expect( (tlast != log_epoch_secs), 0)) {
struct tm tm;
tlast = log_epoch_secs;
tlen = strftime(tstr, sizeof(tstr),
"%Y-%m-%d %H:%M:%S", localtime(&tlast));
tlen = (uint32_t)
strftime(tstr, sizeof(tstr), "%Y-%m-%d %H:%M:%S",
localtime_r(&tlast, &tm));
tstr[ tlen] = ':';
tstr[++tlen] = ' ';
tstr[++tlen] = '(';
/*tstr[++tlen] = '\0';*//*(not necessary for our use)*/
}
buffer_copy_string_len(b, tstr, tlen);
buffer_append_string_len(b, CONST_STR_LEN(": ("));
break;
case ERRORLOG_SYSLOG:
/* syslog is generating its own timestamps */

19
src/mod_accesslog.c

@ -1,5 +1,7 @@
#include "first.h"
#include "sys-time.h"
#include "base.h"
#include "fdevent.h"
#include "log.h"
@ -16,7 +18,6 @@
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#ifdef HAVE_SYSLOG_H
# include <syslog.h>
@ -871,15 +872,7 @@ static int log_access_record (const request_st * const r, buffer * const b, form
/* cache the generated timestamp (only if ! FORMAT_FLAG_TIME_BEGIN) */
struct tm *tmptr;
time_t t;
#if defined(HAVE_STRUCT_TM_GMTOFF)
# ifdef HAVE_LOCALTIME_R
struct tm tm;
# endif /* HAVE_LOCALTIME_R */
#else /* HAVE_STRUCT_TM_GMTOFF */
# ifdef HAVE_GMTIME_R
struct tm tm;
# endif /* HAVE_GMTIME_R */
#endif /* HAVE_STRUCT_TM_GMTOFF */
if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
const time_t cur_ts = log_epoch_secs;
@ -894,17 +887,9 @@ static int log_access_record (const request_st * const r, buffer * const b, form
}
#if defined(HAVE_STRUCT_TM_GMTOFF)
# ifdef HAVE_LOCALTIME_R
tmptr = localtime_r(&t, &tm);
# else /* HAVE_LOCALTIME_R */
tmptr = localtime(&t);
# endif /* HAVE_LOCALTIME_R */
#else /* HAVE_STRUCT_TM_GMTOFF */
# ifdef HAVE_GMTIME_R
tmptr = gmtime_r(&t, &tm);
# else /* HAVE_GMTIME_R */
tmptr = gmtime(&t);
# endif /* HAVE_GMTIME_R */
#endif /* HAVE_STRUCT_TM_GMTOFF */
buffer_clear(ts_accesslog_str);

8
src/mod_cml_lua.c

@ -278,17 +278,13 @@ int cache_parse_lua(request_st * const r, plugin_data * const p, const buffer *
if (ret == 0) {
const buffer *vb = http_header_response_get(r, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"));
if (NULL == vb) { /* no Last-Modified specified */
char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
if (0 == mtime) mtime = time(NULL); /* default last-modified to now */
strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
http_header_response_set(r, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
vb = http_header_response_get(r, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"));
force_assert(NULL != vb);
vb = http_response_set_last_modified(r, mtime);
}
r->resp_body_finished = 1;
if (HANDLER_FINISHED == http_response_handle_cachable(r, vb)) {
if (HANDLER_FINISHED == http_response_handle_cachable(r, vb, mtime)) {
/* ok, the client already has our content,
* no need to send it again */

23
src/mod_dirlisting.c

@ -1,5 +1,7 @@
#include "first.h"
#include "sys-time.h"
#include "base.h"
#include "log.h"
#include "buffer.h"
@ -16,7 +18,6 @@
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#ifdef HAVE_PCRE_H
#include <pcre.h>
@ -822,13 +823,10 @@ static int http_list_directory(request_st * const r, plugin_data * const p, buff
dirls_list_t dirs, files, *list;
dirls_entry_t *tmp;
char sizebuf[sizeof("999.9K")];
char datebuf[sizeof("2005-Jan-01 22:23:24")];
const buffer *content_type;
long name_max;
log_error_st * const errh = r->conf.errh;
#ifdef HAVE_LOCALTIME_R
struct tm tm;
#endif
if (buffer_string_is_empty(dir)) return -1;
@ -939,19 +937,12 @@ static int http_list_directory(request_st * const r, plugin_data * const p, buff
for (i = 0; i < dirs.used; i++) {
tmp = dirs.ent[i];
#ifdef HAVE_LOCALTIME_R
localtime_r(&(tmp->mtime), &tm);
strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm);
#else
strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime)));
#endif
buffer_append_string_len(out, CONST_STR_LEN("<tr class=\"d\"><td class=\"n\"><a href=\""));
buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART);
buffer_append_string_len(out, CONST_STR_LEN("/\">"));
buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML);
buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">"));
buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1);
buffer_append_strftime(out, "%Y-%b-%d %H:%M:%S", localtime_r(&tmp->mtime, &tm));
buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- &nbsp;</td><td class=\"t\">Directory</td></tr>\n"));
free(tmp);
@ -978,12 +969,6 @@ static int http_list_directory(request_st * const r, plugin_data * const p, buff
content_type = &octet_stream;
}
#ifdef HAVE_LOCALTIME_R
localtime_r(&(tmp->mtime), &tm);
strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm);
#else
strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime)));
#endif
http_list_directory_sizefmt(sizebuf, sizeof(sizebuf), tmp->size);
buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\""));
@ -991,7 +976,7 @@ static int http_list_directory(request_st * const r, plugin_data * const p, buff
buffer_append_string_len(out, CONST_STR_LEN("\">"));
buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML);
buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">"));
buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1);
buffer_append_strftime(out, "%Y-%b-%d %H:%M:%S", localtime_r(&tmp->mtime, &tm));
buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">"));
buffer_append_string(out, sizebuf);
buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"t\">"));

5
src/mod_expire.c

@ -9,9 +9,9 @@
#include "plugin.h"
#include "stat_cache.h"
#include "sys-time.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
/**
* set HTTP headers Cache-Control and Expires
@ -314,10 +314,11 @@ REQUEST_FUNC(mod_expire_handler) {
if (expires < cur_ts) expires = cur_ts;
buffer * const tb = r->tmp_buf;
struct tm tm;
/* HTTP/1.0 */
buffer_clear(tb);
buffer_append_strftime(tb, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(expires)));
buffer_append_strftime(tb, "%a, %d %b %Y %H:%M:%S GMT", gmtime_r(&expires, &tm));
http_header_response_set(r, HTTP_HEADER_EXPIRES,
CONST_STR_LEN("Expires"),
CONST_BUF_LEN(tb));

61
src/mod_ssi.c

@ -14,6 +14,7 @@
#include "mod_ssi.h"
#include "sys-socket.h"
#include "sys-time.h"
#include <sys/types.h>
#include <sys/stat.h>
@ -26,7 +27,6 @@
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#ifdef HAVE_PWD_H
@ -385,34 +385,22 @@ static int process_ssi_stmt(request_st * const r, handler_ctx * const p, const c
chunkqueue_append_mem(cq, CONST_BUF_LEN(tb));
break;
}
case SSI_ECHO_LAST_MODIFIED: {
time_t t = st->st_mtime;
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
chunkqueue_append_mem(cq, CONST_STR_LEN("(none)"));
} else {
chunkqueue_append_mem(cq, buf, strlen(buf));
}
break;
}
case SSI_ECHO_DATE_LOCAL: {
time_t t = time(NULL);
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
chunkqueue_append_mem(cq, CONST_STR_LEN("(none)"));
} else {
chunkqueue_append_mem(cq, buf, strlen(buf));
}
break;
}
case SSI_ECHO_LAST_MODIFIED:
case SSI_ECHO_DATE_LOCAL:
case SSI_ECHO_DATE_GMT: {
time_t t = time(NULL);
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) {
struct tm tm;
time_t t = (var == SSI_ECHO_LAST_MODIFIED)
? st->st_mtime
: time(NULL);
uint32_t len = strftime(buf, sizeof(buf), p->timefmt->ptr,
(var != SSI_ECHO_DATE_GMT)
? localtime_r(&t, &tm)
: gmtime_r(&t, &tm));
if (len)
chunkqueue_append_mem(cq, buf, len);
else
chunkqueue_append_mem(cq, CONST_STR_LEN("(none)"));
} else {
chunkqueue_append_mem(cq, buf, strlen(buf));
}
break;
}
case SSI_ECHO_DOCUMENT_NAME: {
@ -611,13 +599,15 @@ static int process_ssi_stmt(request_st * const r, handler_ctx * const p, const c
}
chunkqueue_append_mem(cq, CONST_BUF_LEN(tb));
break;
case SSI_FLASTMOD:
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
case SSI_FLASTMOD: {
struct tm tm;
uint32_t len = (uint32_t)strftime(buf, sizeof(buf), p->timefmt->ptr, localtime_r(&t, &tm));
if (len)
chunkqueue_append_mem(cq, buf, len);
else
chunkqueue_append_mem(cq, CONST_STR_LEN("(none)"));
} else {
chunkqueue_append_mem(cq, buf, strlen(buf));
}
break;
}
case SSI_INCLUDE:
/* Keep the newest mtime of included files */
if (stb.st_mtime > include_file_last_mtime)
@ -1214,7 +1204,6 @@ static int mod_ssi_handle_request(request_st * const r, handler_ctx * const p) {
if (p->conf.conditional_requests) {
/* Generate "ETag" & "Last-Modified" headers */
const buffer *mtime = NULL;
/* use most recently modified include file for ETag and Last-Modified */
if (st.st_mtime < include_file_last_mtime)
@ -1224,10 +1213,8 @@ static int mod_ssi_handle_request(request_st * const r, handler_ctx * const p) {
etag_mutate(&r->physical.etag, &r->physical.etag);
http_header_response_set(r, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(&r->physical.etag));
mtime = strftime_cache_get(st.st_mtime);
http_header_response_set(r, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
if (HANDLER_FINISHED == http_response_handle_cachable(r, mtime)) {
const buffer * const mtime = http_response_set_last_modified(r, st.st_mtime);
if (HANDLER_FINISHED == http_response_handle_cachable(r, mtime, st.st_mtime)) {
/* ok, the client already has our content,
* no need to send it again */

6
src/mod_status.c

@ -9,11 +9,11 @@
#include "plugin.h"
#include <sys/types.h>
#include "sys-time.h"
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdio.h>
typedef struct {
@ -474,8 +474,8 @@ static handler_t mod_status_handle_server_status_html(server *srv, request_st *
ts = srv->startup_ts;
strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts));
buffer_append_string(b, buf);
struct tm tm;
buffer_append_strftime(b, "%Y-%m-%d %H:%M:%S", localtime_r(&ts, &tm));
buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));

21
src/mod_webdav.c

@ -174,6 +174,7 @@
#include "sys-mmap.h"
#include <sys/types.h>
#include <sys/stat.h>
#include "sys-time.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@ -229,6 +230,7 @@
#include "buffer.h"
#include "chunk.h"
#include "fdevent.h"
#include "http_date.h"
#include "http_header.h"
#include "etag.h"
#include "log.h"
@ -2221,11 +2223,8 @@ webdav_if_match_or_unmodified_since (request_st * const r, struct stat *st)
if (NULL != ius) {
if (NULL == st)
return 412; /* Precondition Failed */
struct tm itm, *ftm = gmtime(&st->st_mtime);
if (NULL == strptime(ius->ptr, "%a, %d %b %Y %H:%M:%S GMT", &itm)
|| mktime(ftm) > mktime(&itm)) { /* timegm() not standard */
if (http_date_if_modified_since(CONST_BUF_LEN(ius), st->st_mtime))
return 412; /* Precondition Failed */
}
}
return 0;
@ -3081,13 +3080,10 @@ webdav_propfind_live_props (const webdav_propfind_bufs * const restrict pb,
* i.e. wherever the status is 201 Created)
*/
struct tm tm;
char ctime_buf[sizeof("2005-08-18T07:27:16Z")];
if (__builtin_expect( (NULL != gmtime_r(&pb->st.st_ctime, &tm)), 1)) {
buffer_append_string_len(b, CONST_STR_LEN(
"<D:creationdate ns0:dt=\"dateTime.tz\">"));
buffer_append_string_len(b, ctime_buf,
strftime(ctime_buf, sizeof(ctime_buf),
"%Y-%m-%dT%TZ", &tm));
buffer_append_strftime(b, "%Y-%m-%dT%TZ", &tm));
buffer_append_string_len(b, CONST_STR_LEN(
"</D:creationdate>"));
}
@ -3179,17 +3175,18 @@ webdav_propfind_live_props (const webdav_propfind_bufs * const restrict pb,
return -1; /* invalid; report 'not found' */
if (pnum != WEBDAV_PROP_ALL) return 0;/* found *//*(else fall through)*/
__attribute_fallthrough__
case WEBDAV_PROP_GETLASTMODIFIED:
{
case WEBDAV_PROP_GETLASTMODIFIED: {
struct tm tm;
if (__builtin_expect( (NULL != gmtime_r(&pb->st.st_mtime, &tm)), 1)) {
buffer_append_string_len(b, CONST_STR_LEN(
"<D:getlastmodified ns0:dt=\"dateTime.rfc1123\">"));
buffer_append_strftime(b, "%a, %d %b %Y %H:%M:%S GMT",
gmtime(&pb->st.st_mtime));
buffer_append_strftime(b, "%a, %d %b %Y %H:%M:%S GMT", &tm);
buffer_append_string_len(b, CONST_STR_LEN(
"</D:getlastmodified>"));
}
if (pnum != WEBDAV_PROP_ALL) return 0;/* found *//*(else fall through)*/
__attribute_fallthrough__
}
#if 0
#ifdef USE_LOCKS
case WEBDAV_PROP_LOCKDISCOVERY:

19
src/response.c

@ -10,6 +10,7 @@
#include "stat_cache.h"
#include "chunk.h"
#include "http_chunk.h"
#include "http_date.h"
#include "plugin.h"
@ -129,21 +130,17 @@ http_response_write_header (request_st * const r)
}
if (!light_btst(r->resp_htags, HTTP_HEADER_DATE)) {
static time_t tlast;
static char tstr[32]; /* 30-chars for "%a, %d %b %Y %H:%M:%S GMT" */
static size_t tlen;
/* HTTP/1.1 and later requires a Date: header */
/* "\r\nDate: " 8-chars + 30-chars "%a, %d %b %Y %H:%M:%S GMT" + '\0' */
static time_t tlast = 0;
static char tstr[40] = "\r\nDate: ";
/* cache the generated timestamp */
const time_t cur_ts = log_epoch_secs;
if (tlast != cur_ts) {
tlast = cur_ts;
tlen = strftime(tstr, sizeof(tstr),
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&tlast));
}
if (__builtin_expect ( (tlast != cur_ts), 0))
http_date_time_to_str(tstr+8, sizeof(tstr)-8, (tlast = cur_ts));
/* HTTP/1.1 and later requires a Date: header */
buffer_append_string_len(b, CONST_STR_LEN("\r\nDate: "));
buffer_append_string_len(b, tstr, tlen);
buffer_append_string_len(b, tstr, 37);
}
if (!light_btst(r->resp_htags, HTTP_HEADER_SERVER)) {

4
src/response.h

@ -53,7 +53,8 @@ handler_t http_response_reqbody_read_error(request_st *r, int http_status);
int http_response_buffer_append_authority(request_st *r, buffer *o);
int http_response_redirect_to_directory(request_st *r, int status);
int http_response_handle_cachable(request_st *r, const buffer *mtime);
const buffer * http_response_set_last_modified(request_st *r, time_t lmtime);
int http_response_handle_cachable(request_st *r, const buffer *lmod, time_t lmtime);
void http_response_body_clear(request_st *r, int preserve_length);
void http_response_reset(request_st *r);
void http_response_send_file (request_st *r, buffer *path);
@ -70,5 +71,4 @@ handler_t http_response_handler(request_st *r);
__attribute_cold__
void strftime_cache_reset(void);
const buffer * strftime_cache_get(time_t last_mod);
#endif

8
src/sys-time.h

@ -6,14 +6,12 @@
*/
#ifndef INCLUDED_SYS_TIME_H
#define INCLUDED_SYS_TIME_H
/* _XOPEN_SOURCE for strptime() */
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 700
#endif
#include "first.h"
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>/* gettimeofday() */
#include <time.h> /* time() localtime_r() gmtime_r() strftime() strptime() */
#endif
#include <time.h> /* time() localtime_r() gmtime_r() strftime() */
/*(provide rudimentary localtime_r() and gmtime_r() for platforms lacking)
*(Note that 'result' preprocessor arg is repeated, so callers should avoid

10
tests/cachable.t

@ -8,7 +8,7 @@ BEGIN {
use strict;
use IO::Socket;
use Test::More tests => 25;
use Test::More tests => 24;
use LightyTest;
my $tf = LightyTest->new();
@ -44,14 +44,6 @@ EOF
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304 } ];
ok($tf->handle_http($t) == 0, 'Conditional GET - new If-Modified-Since');
$t->{REQUEST} = ( <<EOF
GET / HTTP/1.0
If-Modified-Since: $now; foo
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304 } ];
ok($tf->handle_http($t) == 0, 'Conditional GET - new If-Modified-Since, comment');
$t->{REQUEST} = ( <<EOF
GET / HTTP/1.0
If-None-Match: foo

Loading…
Cancel
Save