From 543bd249fb385c8ec0ec30b24eafb92b6933a223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Tue, 22 Jan 2013 13:08:21 +0000 Subject: [PATCH] fix handling of If-Modified-Since if If-None-Match is present (don't return 412 for date parsing errors); follow current draft for HTTP/1.1, which tells us to ignore If-Modified-Since if we have matching etags. See: http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-21#section-5 > it makes sense to ignore the If-Modified-Since when entity tags are > understood and available for the selected representation. git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2865 152afb58-edef-0310-8abb-c4023f1b3aa9 --- NEWS | 2 ++ src/http-header-glue.c | 62 ++++-------------------------------------- tests/cachable.t | 10 +++---- 3 files changed, 13 insertions(+), 61 deletions(-) diff --git a/NEWS b/NEWS index 17dceb1f..3eed4a36 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ NEWS - 1.4.33 - * mod_fastcgi: fix mix up of "mode" => "authorizer" in other fastcgi configs (fixes #2465, thx peex) + * fix handling of If-Modified-Since if If-None-Match is present (don't return 412 for date parsing errors); + follow current draft for HTTP/1.1, which tells us to ignore If-Modified-Since if we have matching etags. - 1.4.32 - 2012-11-21 * Code cleanup with clang/sparse (fixes #2437, thx kibi) diff --git a/src/http-header-glue.c b/src/http-header-glue.c index a33ed3be..4a74c0ee 100644 --- a/src/http-header-glue.c +++ b/src/http-header-glue.c @@ -245,6 +245,7 @@ buffer * strftime_cache_get(server *srv, time_t last_mod) { int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) { + UNUSED(srv); /* * 14.26 If-None-Match * [...] @@ -261,68 +262,17 @@ int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) { if (con->request.http_method == HTTP_METHOD_GET || con->request.http_method == HTTP_METHOD_HEAD) { - /* check if etag + last-modified */ - if (con->request.http_if_modified_since) { - size_t used_len; - char *semicolon; - - if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) { - used_len = strlen(con->request.http_if_modified_since); - } else { - used_len = semicolon - con->request.http_if_modified_since; - } - - if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) { - if ('\0' == mtime->ptr[used_len]) con->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; - - /* check if we can safely copy the string */ - if (used_len >= sizeof(buf)) { - log_error_write(srv, __FILE__, __LINE__, "ssdd", - "DEBUG: Last-Modified check failed as the received timestamp was too long:", - con->request.http_if_modified_since, used_len, sizeof(buf) - 1); - - con->http_status = 412; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - - strncpy(buf, con->request.http_if_modified_since, used_len); - buf[used_len] = '\0'; - - if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) { - con->http_status = 412; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - 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; - - con->http_status = 304; - return HANDLER_FINISHED; - } - } else { - con->http_status = 304; - return HANDLER_FINISHED; - } + con->http_status = 304; + return HANDLER_FINISHED; } else { con->http_status = 412; con->mode = DIRECT; return HANDLER_FINISHED; } } - } else if (con->request.http_if_modified_since) { + } else if (con->request.http_if_modified_since && + (con->request.http_method == HTTP_METHOD_GET || + con->request.http_method == HTTP_METHOD_HEAD)) { size_t used_len; char *semicolon; diff --git a/tests/cachable.t b/tests/cachable.t index 605a68f1..381f44e0 100755 --- a/tests/cachable.t +++ b/tests/cachable.t @@ -78,8 +78,8 @@ If-None-Match: $etag If-Modified-Since: Sun, 01 Jan 1970 00:00:01 GMT; foo EOF ); -$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; -ok($tf->handle_http($t) == 0, 'Conditional GET - ETag + old Last-Modified'); +$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304 } ]; +ok($tf->handle_http($t) == 0, 'Conditional GET - ETag + old Last-Modified (which should be ignored)'); $t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304 } ]; -ok($tf->handle_http($t) == 0, 'Conditional GET - ETag, Last-Modified + comment'); +ok($tf->handle_http($t) == 0, 'Conditional GET - ETag, Last-Modified + comment (which should be ignored)'); $t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 412 } ]; -ok($tf->handle_http($t) == 0, 'Conditional GET - ETag + Last-Modified + overlong timestamp'); +$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304 } ]; +ok($tf->handle_http($t) == 0, 'Conditional GET - ETag + Last-Modified + overlong timestamp (which should be ignored)'); $t->{REQUEST} = ( <