From 45dbbc39f69bd530fa9eca6c05a462bc022992fd Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 16 Aug 2014 22:25:55 -0400 Subject: [PATCH] Additional fix to #4728. --- docs/guide/caching-http.md | 8 +++----- framework/filters/HttpCache.php | 11 +++++++---- tests/unit/framework/filters/HttpCacheTest.php | 11 ++++++++--- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/guide/caching-http.md b/docs/guide/caching-http.md index 76980b5993..e9682eecbf 100644 --- a/docs/guide/caching-http.md +++ b/docs/guide/caching-http.md @@ -107,12 +107,10 @@ Expensive ETag generation may defeat the purpose of using `HttpCache` and introd since they need to be re-evaluated on every request. Try to find a simple expression that invalidates the cache if the page content has been modified. - -> Note: In compliance to [RFC 7232, section 2.4](http://tools.ietf.org/html/rfc7232#section-2.4), +> Note: In compliance to [RFC 7232](http://tools.ietf.org/html/rfc7232#section-2.4), `HttpCache` will send out both `ETag` and `Last-Modified` headers if they are both configured. - However, in order to satisfy [section 3.3](http://tools.ietf.org/html/rfc7232#section-3.3) the - `If-None-Match` client header will always take precedence over `If-Modified-Since` during validation; meaning - latter one is going to be ignored if a `If-None-Match` header is present in the request. + And if the client sends both of the `If-None-Match` header and the `If-Modified-Since` header, only the former + will be respected. ## `Cache-Control` Header diff --git a/framework/filters/HttpCache.php b/framework/filters/HttpCache.php index c3f99c1aa3..11117f87e2 100644 --- a/framework/filters/HttpCache.php +++ b/framework/filters/HttpCache.php @@ -130,7 +130,6 @@ class HttpCache extends ActionFilter if ($this->validateCache($lastModified, $etag)) { $response->setStatusCode(304); - return false; } @@ -150,10 +149,14 @@ class HttpCache extends ActionFilter */ protected function validateCache($lastModified, $etag) { - if($etag !== null && in_array($etag, Yii::$app->request->getEtags(), true)) { - return true; + if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) { + // HTTP_IF_NONE_MATCH takes precedence over HTTP_IF_MODIFIED_SINCE + // http://tools.ietf.org/html/rfc7232#section-3.3 + return $etag === null || in_array($etag, Yii::$app->request->getEtags(), true); + } elseif (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { + return $lastModified === null || @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $lastModified; } else { - return $lastModified !== null && isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $lastModified; + return $etag === null && $lastModified === null; } } diff --git a/tests/unit/framework/filters/HttpCacheTest.php b/tests/unit/framework/filters/HttpCacheTest.php index 440c3ecbca..96fc4843a3 100644 --- a/tests/unit/framework/filters/HttpCacheTest.php +++ b/tests/unit/framework/filters/HttpCacheTest.php @@ -37,7 +37,10 @@ class HttpCacheTest extends \yiiunit\TestCase $method = new \ReflectionMethod($httpCache, 'validateCache'); $method->setAccessible(true); - $this->assertFalse($method->invoke($httpCache, null, null)); + unset($_SERVER['HTTP_IF_MODIFIED_SINCE'], $_SERVER['HTTP_IF_NONE_MATCH']); + $this->assertTrue($method->invoke($httpCache, null, null)); + $this->assertFalse($method->invoke($httpCache, 0, null)); + $this->assertFalse($method->invoke($httpCache, 0, '"foo"')); $_SERVER['HTTP_IF_MODIFIED_SINCE'] = 'Thu, 01 Jan 1970 00:00:00 GMT'; $this->assertTrue($method->invoke($httpCache, 0, null)); @@ -45,8 +48,10 @@ class HttpCacheTest extends \yiiunit\TestCase $_SERVER['HTTP_IF_NONE_MATCH'] = '"foo"'; $this->assertTrue($method->invoke($httpCache, 0, '"foo"')); + $this->assertFalse($method->invoke($httpCache, 0, '"foos"')); $this->assertTrue($method->invoke($httpCache, 1, '"foo"')); - $this->assertFalse($method->invoke($httpCache, null, null)); + $this->assertFalse($method->invoke($httpCache, 1, '"foos"')); + $this->assertTrue($method->invoke($httpCache, null, null)); } /** @@ -67,4 +72,4 @@ class HttpCacheTest extends \yiiunit\TestCase $this->assertStringStartsWith('"', $etag); $this->assertStringEndsWith('"', $etag); } -} \ No newline at end of file +}