From 87430ffc5aef789018892ee23dc6aea1bd5a8c23 Mon Sep 17 00:00:00 2001 From: Volker Killesreiter Date: Tue, 30 Mar 2021 17:03:07 +0200 Subject: [PATCH 1/8] up version --- src/Symfony/Component/HttpKernel/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index a2fa2a0ecac5d..2b4e695bb5cbd 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -20,7 +20,7 @@ "symfony/error-handler": "^4.4", "symfony/event-dispatcher": "^4.4", "symfony/http-client-contracts": "^1.1|^2", - "symfony/http-foundation": "^4.4|^5.0", + "symfony/http-foundation": "^4.4.22|^5.2.7", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-php73": "^1.9", "symfony/polyfill-php80": "^1.15", From 505914574ccd30c2e0e98abc19d4923846e303f0 Mon Sep 17 00:00:00 2001 From: Volker Killesreiter Date: Tue, 30 Mar 2021 18:19:51 +0200 Subject: [PATCH 2/8] fix logic, add tests --- .../Component/HttpFoundation/Response.php | 25 ++++++++++--- .../HttpFoundation/Tests/ResponseTest.php | 36 ++++++++++++++++++- .../Tests/HttpCache/HttpCacheTest.php | 2 +- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 4ab97aaf90538..3c0950f3384cf 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -1079,12 +1079,27 @@ public function isNotModified(Request $request): bool $lastModified = $this->headers->get('Last-Modified'); $modifiedSince = $request->headers->get('If-Modified-Since'); - if ($etags = $request->getETags()) { - $notModified = \in_array($this->getEtag(), $etags) || \in_array('*', $etags); - } + if ($ifNoneMatchEtags = $request->getETags()) { + $etag = $this->getEtag(); + if (0 == strncmp($etag, 'W/', 2)) { + $etag = substr($etag, 2); + } + + // Use weak comparison as per https://tools.ietf.org/html/rfc7232#section-3.2. + foreach ($ifNoneMatchEtags as $ifNoneMatchEtag) { + if (0 == strncmp($ifNoneMatchEtag, 'W/', 2)) { + $ifNoneMatchEtag = substr($ifNoneMatchEtag, 2); + } - if ($modifiedSince && $lastModified) { - $notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified); + if ($ifNoneMatchEtag === $etag || '*' === $ifNoneMatchEtag) { + $notModified = true; + break; + } + } + } + // Only do If-Modified-Since date comparison when If-None-Match is not present as per https://tools.ietf.org/html/rfc7232#section-3.3. + elseif ($modifiedSince && $lastModified) { + $notModified = strtotime($modifiedSince) >= strtotime($lastModified); } if ($notModified) { diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index f26302eef1120..6545e1b67a82e 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -206,8 +206,42 @@ public function testIsNotModifiedEtag() $response->headers->set('ETag', ''); $this->assertFalse($response->isNotModified($request)); + + // Test wildcard + $request = new Request(); + $request->headers->set('if_none_match', '*'); + + $response->headers->set('ETag', $etagOne); + $this->assertTrue($response->isNotModified($request)); } + public function testIsNotModifiedWeakEtag() + { + $etag = 'randomly_generated_etag'; + $weakEtag = 'W/randomly_generated_etag'; + + $request = new Request(); + #$request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree')); + $request->headers->set('if_none_match', $etag); + $response = new Response(); + + $response->headers->set('ETag', $etag); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('ETag', $weakEtag); + $this->assertTrue($response->isNotModified($request)); + + $request->headers->set('if_none_match', $weakEtag); + $response = new Response(); + + $response->headers->set('ETag', $etag); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('ETag', $weakEtag); + $this->assertTrue($response->isNotModified($request)); + } + + public function testIsNotModifiedLastModifiedAndEtag() { $before = 'Sun, 25 Aug 2013 18:32:31 GMT'; @@ -223,7 +257,7 @@ public function testIsNotModifiedLastModifiedAndEtag() $response->headers->set('ETag', $etag); $response->headers->set('Last-Modified', $after); - $this->assertFalse($response->isNotModified($request)); + $this->assertTrue($response->isNotModified($request)); $response->headers->set('ETag', 'non-existent-etag'); $response->headers->set('Last-Modified', $before); diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index 9bec2f7c94eb1..a3e7b9255ebfd 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -172,7 +172,7 @@ public function testRespondsWith304OnlyIfIfNoneMatchAndIfModifiedSinceBothMatch( $t = \DateTime::createFromFormat('U', time() - 3600); $this->request('GET', '/', ['HTTP_IF_NONE_MATCH' => '12345', 'HTTP_IF_MODIFIED_SINCE' => $t->format(\DATE_RFC2822)]); $this->assertHttpKernelIsCalled(); - $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals(304, $this->response->getStatusCode()); // only Last-Modified matches $this->request('GET', '/', ['HTTP_IF_NONE_MATCH' => '1234', 'HTTP_IF_MODIFIED_SINCE' => $time->format(\DATE_RFC2822)]); From 9eb7eb5435512a8227c8b703a6a0ff81a439a735 Mon Sep 17 00:00:00 2001 From: Volker Killesreiter Date: Tue, 30 Mar 2021 18:22:19 +0200 Subject: [PATCH 3/8] fix cs --- src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index 6545e1b67a82e..d906ccb06c9ae 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -221,7 +221,6 @@ public function testIsNotModifiedWeakEtag() $weakEtag = 'W/randomly_generated_etag'; $request = new Request(); - #$request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree')); $request->headers->set('if_none_match', $etag); $response = new Response(); @@ -241,7 +240,6 @@ public function testIsNotModifiedWeakEtag() $this->assertTrue($response->isNotModified($request)); } - public function testIsNotModifiedLastModifiedAndEtag() { $before = 'Sun, 25 Aug 2013 18:32:31 GMT'; From a7d701d91b3fd45683e8d950b592aeb17949cc09 Mon Sep 17 00:00:00 2001 From: Volker Killesreiter Date: Mon, 12 Apr 2021 09:23:45 +0200 Subject: [PATCH 4/8] Renamed test --- .../Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index a3e7b9255ebfd..fc363862ea680 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -156,7 +156,7 @@ public function testRespondsWith304WhenIfNoneMatchMatchesETag() $this->assertTraceContains('store'); } - public function testRespondsWith304OnlyIfIfNoneMatchAndIfModifiedSinceBothMatch() + public function testRespondsWith304WhenIfNoneMatchAndIfModifiedSinceBothMatch() { $time = \DateTime::createFromFormat('U', time()); From 0293524efb1d5da3f50004a1c6d94367690db33b Mon Sep 17 00:00:00 2001 From: Volker Killesreiter Date: Mon, 12 Apr 2021 19:51:00 +0200 Subject: [PATCH 5/8] Refactor request header handling for if_none_match and if_modified_since to If-None-Match and If-Modified-Since respectively. --- src/Symfony/Component/HttpFoundation/Request.php | 2 +- .../Component/HttpFoundation/Tests/ResponseTest.php | 12 ++++++------ .../Component/HttpKernel/HttpCache/HttpCache.php | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index cf6ebbc0b0903..98e2efef45292 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1564,7 +1564,7 @@ public function getContent($asResource = false) */ public function getETags() { - return preg_split('/\s*,\s*/', $this->headers->get('if_none_match', ''), -1, \PREG_SPLIT_NO_EMPTY); + return preg_split('/\s*,\s*/', $this->headers->get('If-None-Match', ''), -1, \PREG_SPLIT_NO_EMPTY); } /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index d906ccb06c9ae..b6d714ae5973d 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -194,7 +194,7 @@ public function testIsNotModifiedEtag() $etagTwo = 'randomly_generated_etag_2'; $request = new Request(); - $request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree')); + $request->headers->set('If-None-Match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree')); $response = new Response(); @@ -209,7 +209,7 @@ public function testIsNotModifiedEtag() // Test wildcard $request = new Request(); - $request->headers->set('if_none_match', '*'); + $request->headers->set('If-None-Match', '*'); $response->headers->set('ETag', $etagOne); $this->assertTrue($response->isNotModified($request)); @@ -221,7 +221,7 @@ public function testIsNotModifiedWeakEtag() $weakEtag = 'W/randomly_generated_etag'; $request = new Request(); - $request->headers->set('if_none_match', $etag); + $request->headers->set('If-None-Match', $etag); $response = new Response(); $response->headers->set('ETag', $etag); @@ -230,7 +230,7 @@ public function testIsNotModifiedWeakEtag() $response->headers->set('ETag', $weakEtag); $this->assertTrue($response->isNotModified($request)); - $request->headers->set('if_none_match', $weakEtag); + $request->headers->set('If-None-Match', $weakEtag); $response = new Response(); $response->headers->set('ETag', $etag); @@ -248,7 +248,7 @@ public function testIsNotModifiedLastModifiedAndEtag() $etag = 'randomly_generated_etag'; $request = new Request(); - $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree')); + $request->headers->set('If-None-Match', sprintf('%s, %s', $etag, 'etagThree')); $request->headers->set('If-Modified-Since', $modified); $response = new Response(); @@ -272,7 +272,7 @@ public function testIsNotModifiedIfModifiedSinceAndEtagWithoutLastModified() $etag = 'randomly_generated_etag'; $request = new Request(); - $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree')); + $request->headers->set('If-None-Match', sprintf('%s, %s', $etag, 'etagThree')); $request->headers->set('If-Modified-Since', $modified); $response = new Response(); diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 6c4715802efda..f6cbbc9aa9165 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -382,7 +382,7 @@ protected function validate(Request $request, Response $entry, $catch = false) // add our cached last-modified validator if ($entry->headers->has('Last-Modified')) { - $subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified')); + $subRequest->headers->set('If-Modified-Since', $entry->headers->get('Last-Modified')); } // Add our cached etag validator to the environment. @@ -391,7 +391,7 @@ protected function validate(Request $request, Response $entry, $catch = false) $cachedEtags = $entry->getEtag() ? [$entry->getEtag()] : []; $requestEtags = $request->getETags(); if ($etags = array_unique(array_merge($cachedEtags, $requestEtags))) { - $subRequest->headers->set('if_none_match', implode(', ', $etags)); + $subRequest->headers->set('If-None-Match', implode(', ', $etags)); } $response = $this->forward($subRequest, $catch, $entry); @@ -444,8 +444,8 @@ protected function fetch(Request $request, $catch = false) } // avoid that the backend sends no content - $subRequest->headers->remove('if_modified_since'); - $subRequest->headers->remove('if_none_match'); + $subRequest->headers->remove('If-Modified-Since'); + $subRequest->headers->remove('If-None-Match'); $response = $this->forward($subRequest, $catch); From 07d51e2d2b4e91eb1b0d996667e30568ad17cbb6 Mon Sep 17 00:00:00 2001 From: Volker Killesreiter Date: Mon, 12 Apr 2021 19:52:29 +0200 Subject: [PATCH 6/8] license headers --- src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index f6cbbc9aa9165..be7042279483a 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + /* * This file is part of the Symfony package. * From fda360846bbe18cc6bd35605589b108d3ee766a1 Mon Sep 17 00:00:00 2001 From: Volker Killesreiter Date: Mon, 17 May 2021 17:25:59 +0200 Subject: [PATCH 7/8] Bump version --- src/Symfony/Component/HttpKernel/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 2b4e695bb5cbd..091352a52d5b6 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -20,7 +20,7 @@ "symfony/error-handler": "^4.4", "symfony/event-dispatcher": "^4.4", "symfony/http-client-contracts": "^1.1|^2", - "symfony/http-foundation": "^4.4.22|^5.2.7", + "symfony/http-foundation": "^4.4.23|^5.2.8", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-php73": "^1.9", "symfony/polyfill-php80": "^1.15", From b639fe644b5057fa1b103dcfa05f9fd5169942ba Mon Sep 17 00:00:00 2001 From: Volker Killesreiter Date: Tue, 18 May 2021 08:56:30 +0200 Subject: [PATCH 8/8] Bump version above latest --- src/Symfony/Component/HttpKernel/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 091352a52d5b6..74ce9aadb05db 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -20,7 +20,7 @@ "symfony/error-handler": "^4.4", "symfony/event-dispatcher": "^4.4", "symfony/http-client-contracts": "^1.1|^2", - "symfony/http-foundation": "^4.4.23|^5.2.8", + "symfony/http-foundation": "^4.4.24|^5.2.9", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-php73": "^1.9", "symfony/polyfill-php80": "^1.15",