For various reasons I won't go in to here I needed finer granularity in the Last-Modified and If-Modified-Since HTTPD headers for a project I am working on. Since Linux 2.5.48 the file modification time has a granularity of nanoseconds - here is the quote from the man page:
Since kernel 2.5.48, the stat structure supports nanosecond resolution for the three file timestamp fields. Glibc exposes the nanosecond component of each field using names either of the form st_atim.tv_nsec, if the _BSD_SOURCE or _SVID_SOURCE feature test macro is defined, or of the form st_atimensec, if neither of these macros is defined.
Originally I made changes for Apache to achieve this (and I can supply them if anyone is really interested) but we are using lighttpd now. The following diff against lighttpd 1.4.26 provides microsecond granularity on Last-Modified and If-Modified-Since. I've not tested all possible lighttpd modules as we are only using the expires one but I believe it is correct - certainly works in our scenario and verified with wget and curl.
diff -ru lighttpd-1.4.26/src/base.h lighttpd-1.4.26.hacked/src/base.h
--- lighttpd-1.4.26/src/base.h 2010-02-01 23:28:20.000000000 +0000
+++ lighttpd-1.4.26.hacked/src/base.h 2010-03-15 16:42:40.198607918 +0000
@@ -468,7 +468,7 @@
} realpath_cache_type;
typedef struct {
- time_t mtime; /* the key */
+ struct timespec mtime; /* the key */
buffer *str; /* a buffer for the string represenation */
} mtime_cache_type;
diff -ru lighttpd-1.4.26/src/http-header-glue.c
lighttpd-1.4.26.hacked/src/http-header-glue.c
--- lighttpd-1.4.26/src/http-header-glue.c 2010-02-01 23:28:20.000000000
+0000
+++ lighttpd-1.4.26.hacked/src/http-header-glue.c 2010-03-19
17:26:45.424258304 +0000
@@ -216,16 +216,20 @@
return 0;
}
-buffer * strftime_cache_get(server *srv, time_t last_mod) {
+buffer * strftime_cache_get(server *srv, struct timespec last_mod) {
struct tm *tm;
- size_t i;
+ size_t i;
+ char nsec[10];
+ char format[50];
for (i = 0; i < FILE_CACHE_MAX; i++) {
/* found cache-entry */
- if (srv->mtime_cache[i].mtime == last_mod) return
srv->mtime_cache[i].str;
+ if ((srv->mtime_cache[i].mtime.tv_nsec == last_mod.tv_nsec) &&
+ (srv->mtime_cache[i].mtime.tv_sec == last_mod.tv_sec))
+ return srv->mtime_cache[i].str;
/* found empty slot */
- if (srv->mtime_cache[i].mtime == 0) break;
+ if (srv->mtime_cache[i].mtime.tv_sec == 0) break;
}
if (i == FILE_CACHE_MAX) {
@@ -234,10 +238,16 @@
srv->mtime_cache[i].mtime = last_mod;
buffer_prepare_copy(srv->mtime_cache[i].str, 1024);
- tm = gmtime(&(srv->mtime_cache[i].mtime));
+ tm = gmtime(&(srv->mtime_cache[i].mtime.tv_sec));
+ sprintf(nsec, "%ld", last_mod.tv_nsec);
+ nsec[6] = '\0';
+ strcpy(format, "%a, %d %b %Y %H:%M:%S.");
+ strcat(format, nsec);
+
srv->mtime_cache[i].str->used = strftime(srv->mtime_cache[i].str->ptr,
srv->mtime_cache[i].str->size
- 1,
- "%a, %d %b %Y %H:%M:%S GMT",
tm);
+ format, tm);
srv->mtime_cache[i].str->used++;
return srv->mtime_cache[i].str;
diff -ru lighttpd-1.4.26/src/mod_compress.c
lighttpd-1.4.26.hacked/src/mod_compress.c
--- lighttpd-1.4.26/src/mod_compress.c 2010-02-01 23:28:20.000000000 +0000
+++ lighttpd-1.4.26.hacked/src/mod_compress.c 2010-03-15 16:38:43.622608346
+0000
@@ -767,7 +767,7 @@
const char *compression_name = NULL;
int compression_type = 0;
- mtime = strftime_cache_get(srv,
sce->st.st_mtime);
+ mtime = strftime_cache_get(srv,
sce->st.st_mtim);
/* try matching original etag of
uncompressed version */
etag_mutate(con->physical.etag,
sce->etag);
diff -ru lighttpd-1.4.26/src/mod_ssi.c lighttpd-1.4.26.hacked/src/mod_ssi.c
--- lighttpd-1.4.26/src/mod_ssi.c 2010-02-01 23:28:20.000000000 +0000
+++ lighttpd-1.4.26.hacked/src/mod_ssi.c 2010-03-19 17:34:47.790610148
+0000
@@ -1084,6 +1084,7 @@
stat_cache_entry *sce = NULL;
time_t lm_time = 0;
buffer *mtime = NULL;
+ struct timespec ts;
stat_cache_get_entry(srv, con, con->physical.path, &sce);
@@ -1095,7 +1096,10 @@
else
lm_time = include_file_last_mtime;
- mtime = strftime_cache_get(srv, lm_time);
+ ts.tv_sec = lm_time;
+ ts.tv_nsec = 0;
+
+ mtime = strftime_cache_get(srv, ts);
response_header_overwrite(srv, con,
CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
}
diff -ru lighttpd-1.4.26/src/mod_staticfile.c
lighttpd-1.4.26.hacked/src/mod_staticfile.c
--- lighttpd-1.4.26/src/mod_staticfile.c 2010-02-01 23:28:20.000000000
+0000
+++ lighttpd-1.4.26.hacked/src/mod_staticfile.c 2010-03-19 17:35:25.150607097
+0000
@@ -466,7 +466,8 @@
/* prepare header */
if (NULL == (ds = (data_string
*)array_get_element(con->response.headers, "Last-Modified"))) {
- mtime = strftime_cache_get(srv, sce->st.st_mtime);
+ mtime = strftime_cache_get(srv, sce->st.st_mtim);
+
response_header_overwrite(srv, con,
CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
} else {
mtime = ds->value;
diff -ru lighttpd-1.4.26/src/response.h lighttpd-1.4.26.hacked/src/response.h
--- lighttpd-1.4.26/src/response.h 2010-02-01 23:28:20.000000000 +0000
+++ lighttpd-1.4.26.hacked/src/response.h 2010-03-15 16:34:48.294609510
+0000
@@ -16,5 +16,5 @@
int http_response_redirect_to_directory(server *srv, connection *con);
int http_response_handle_cachable(server *srv, connection *con, buffer *
mtime);
-buffer * strftime_cache_get(server *srv, time_t last_mod);
+buffer * strftime_cache_get(server *srv, struct timespec last_mod);
#endif
diff -ru lighttpd-1.4.26/src/server.c lighttpd-1.4.26.hacked/src/server.c
--- lighttpd-1.4.26/src/server.c 2010-02-01 23:28:20.000000000 +0000
+++ lighttpd-1.4.26.hacked/src/server.c 2010-03-15 16:45:07.851315667 +0000
@@ -207,7 +207,8 @@
#undef CLEAN
for (i = 0; i < FILE_CACHE_MAX; i++) {
- srv->mtime_cache[i].mtime = (time_t)-1;
+ srv->mtime_cache[i].mtime.tv_sec = (time_t)-1;
+ srv->mtime_cache[i].mtime.tv_nsec = (time_t)-1;
srv->mtime_cache[i].str = buffer_init();
}
Trackback URL for this post:
http://martin-evans.me.uk/trackback/62
Recent comments
31 weeks 1 day ago
33 weeks 4 days ago
35 weeks 1 day ago
35 weeks 2 days ago
43 weeks 3 days ago
44 weeks 4 days ago
45 weeks 6 days ago
48 weeks 5 days ago
1 year 5 days ago
1 year 3 weeks ago