Skip to content

Commit f9ce68e

Browse files
ol0lllfabpot
authored andcommitted
[HttpFoundation] Fix isNotModified determination logic
1 parent 2ffb43b commit f9ce68e

File tree

3 files changed

+57
-10
lines changed

3 files changed

+57
-10
lines changed

Request.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1564,7 +1564,7 @@ public function getContent($asResource = false)
15641564
*/
15651565
public function getETags()
15661566
{
1567-
return preg_split('/\s*,\s*/', $this->headers->get('if_none_match', ''), -1, \PREG_SPLIT_NO_EMPTY);
1567+
return preg_split('/\s*,\s*/', $this->headers->get('If-None-Match', ''), -1, \PREG_SPLIT_NO_EMPTY);
15681568
}
15691569

15701570
/**

Response.php

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,12 +1079,27 @@ public function isNotModified(Request $request): bool
10791079
$lastModified = $this->headers->get('Last-Modified');
10801080
$modifiedSince = $request->headers->get('If-Modified-Since');
10811081

1082-
if ($etags = $request->getETags()) {
1083-
$notModified = \in_array($this->getEtag(), $etags) || \in_array('*', $etags);
1084-
}
1082+
if ($ifNoneMatchEtags = $request->getETags()) {
1083+
$etag = $this->getEtag();
1084+
if (0 == strncmp($etag, 'W/', 2)) {
1085+
$etag = substr($etag, 2);
1086+
}
1087+
1088+
// Use weak comparison as per https://tools.ietf.org/html/rfc7232#section-3.2.
1089+
foreach ($ifNoneMatchEtags as $ifNoneMatchEtag) {
1090+
if (0 == strncmp($ifNoneMatchEtag, 'W/', 2)) {
1091+
$ifNoneMatchEtag = substr($ifNoneMatchEtag, 2);
1092+
}
10851093

1086-
if ($modifiedSince && $lastModified) {
1087-
$notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified);
1094+
if ($ifNoneMatchEtag === $etag || '*' === $ifNoneMatchEtag) {
1095+
$notModified = true;
1096+
break;
1097+
}
1098+
}
1099+
}
1100+
// 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.
1101+
elseif ($modifiedSince && $lastModified) {
1102+
$notModified = strtotime($modifiedSince) >= strtotime($lastModified);
10881103
}
10891104

10901105
if ($notModified) {

Tests/ResponseTest.php

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public function testIsNotModifiedEtag()
194194
$etagTwo = 'randomly_generated_etag_2';
195195

196196
$request = new Request();
197-
$request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree'));
197+
$request->headers->set('If-None-Match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree'));
198198

199199
$response = new Response();
200200

@@ -206,6 +206,38 @@ public function testIsNotModifiedEtag()
206206

207207
$response->headers->set('ETag', '');
208208
$this->assertFalse($response->isNotModified($request));
209+
210+
// Test wildcard
211+
$request = new Request();
212+
$request->headers->set('If-None-Match', '*');
213+
214+
$response->headers->set('ETag', $etagOne);
215+
$this->assertTrue($response->isNotModified($request));
216+
}
217+
218+
public function testIsNotModifiedWeakEtag()
219+
{
220+
$etag = 'randomly_generated_etag';
221+
$weakEtag = 'W/randomly_generated_etag';
222+
223+
$request = new Request();
224+
$request->headers->set('If-None-Match', $etag);
225+
$response = new Response();
226+
227+
$response->headers->set('ETag', $etag);
228+
$this->assertTrue($response->isNotModified($request));
229+
230+
$response->headers->set('ETag', $weakEtag);
231+
$this->assertTrue($response->isNotModified($request));
232+
233+
$request->headers->set('If-None-Match', $weakEtag);
234+
$response = new Response();
235+
236+
$response->headers->set('ETag', $etag);
237+
$this->assertTrue($response->isNotModified($request));
238+
239+
$response->headers->set('ETag', $weakEtag);
240+
$this->assertTrue($response->isNotModified($request));
209241
}
210242

211243
public function testIsNotModifiedLastModifiedAndEtag()
@@ -216,14 +248,14 @@ public function testIsNotModifiedLastModifiedAndEtag()
216248
$etag = 'randomly_generated_etag';
217249

218250
$request = new Request();
219-
$request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree'));
251+
$request->headers->set('If-None-Match', sprintf('%s, %s', $etag, 'etagThree'));
220252
$request->headers->set('If-Modified-Since', $modified);
221253

222254
$response = new Response();
223255

224256
$response->headers->set('ETag', $etag);
225257
$response->headers->set('Last-Modified', $after);
226-
$this->assertFalse($response->isNotModified($request));
258+
$this->assertTrue($response->isNotModified($request));
227259

228260
$response->headers->set('ETag', 'non-existent-etag');
229261
$response->headers->set('Last-Modified', $before);
@@ -240,7 +272,7 @@ public function testIsNotModifiedIfModifiedSinceAndEtagWithoutLastModified()
240272
$etag = 'randomly_generated_etag';
241273

242274
$request = new Request();
243-
$request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree'));
275+
$request->headers->set('If-None-Match', sprintf('%s, %s', $etag, 'etagThree'));
244276
$request->headers->set('If-Modified-Since', $modified);
245277

246278
$response = new Response();

0 commit comments

Comments
 (0)