Skip to content

Commit ebc713b

Browse files
dunglasnicolas-grekas
authored andcommitted
[HttpFoundation] Prevent duplicated headers when using Early Hints
1 parent d68e699 commit ebc713b

File tree

3 files changed

+69
-14
lines changed

3 files changed

+69
-14
lines changed

Response.php

+11-13
Original file line numberDiff line numberDiff line change
@@ -355,23 +355,21 @@ public function sendHeaders(/* int $statusCode = null */): static
355355
$replace = false;
356356

357357
// As recommended by RFC 8297, PHP automatically copies headers from previous 103 responses, we need to deal with that if headers changed
358-
if (103 === $statusCode) {
359-
$previousValues = $this->sentHeaders[$name] ?? null;
360-
if ($previousValues === $values) {
361-
// Header already sent in a previous response, it will be automatically copied in this response by PHP
362-
continue;
363-
}
358+
$previousValues = $this->sentHeaders[$name] ?? null;
359+
if ($previousValues === $values) {
360+
// Header already sent in a previous response, it will be automatically copied in this response by PHP
361+
continue;
362+
}
364363

365-
$replace = 0 === strcasecmp($name, 'Content-Type');
364+
$replace = 0 === strcasecmp($name, 'Content-Type');
366365

367-
if (null !== $previousValues && array_diff($previousValues, $values)) {
368-
header_remove($name);
369-
$previousValues = null;
370-
}
371-
372-
$newValues = null === $previousValues ? $values : array_diff($values, $previousValues);
366+
if (null !== $previousValues && array_diff($previousValues, $values)) {
367+
header_remove($name);
368+
$previousValues = null;
373369
}
374370

371+
$newValues = null === $previousValues ? $values : array_diff($values, $previousValues);
372+
375373
foreach ($newValues as $value) {
376374
header($name.': '.$value, $replace, $this->statusCode);
377375
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
// Requires FrankenPHP
4+
5+
use Symfony\Component\HttpFoundation\Response;
6+
7+
$parent = __DIR__;
8+
while (!@file_exists($parent.'/vendor/autoload.php')) {
9+
if (!@file_exists($parent)) {
10+
// open_basedir restriction in effect
11+
break;
12+
}
13+
if ($parent === dirname($parent)) {
14+
echo "vendor/autoload.php not found\n";
15+
exit(1);
16+
}
17+
18+
$parent = dirname($parent);
19+
}
20+
21+
require $parent.'/vendor/autoload.php';
22+
23+
$r = new Response();
24+
$r->headers->set('Link', '</css/style.css>; rel="preload"; as="style"');
25+
$r->sendHeaders(103);
26+
27+
$r->headers->set('Link', '</js/app.js>; rel="preload"; as="script"', false);
28+
$r->sendHeaders(103);
29+
30+
$r->setContent('Hello, Early Hints');
31+
$r->send();

Tests/ResponseFunctionalTest.php

+27-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
namespace Symfony\Component\HttpFoundation\Tests;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Process\ExecutableFinder;
16+
use Symfony\Component\Process\Process;
1517

1618
class ResponseFunctionalTest extends TestCase
1719
{
@@ -51,7 +53,31 @@ public function testCookie($fixture)
5153
public static function provideCookie()
5254
{
5355
foreach (glob(__DIR__.'/Fixtures/response-functional/*.php') as $file) {
54-
yield [pathinfo($file, \PATHINFO_FILENAME)];
56+
if (str_contains($file, 'cookie')) {
57+
yield [pathinfo($file, \PATHINFO_FILENAME)];
58+
}
5559
}
5660
}
61+
62+
/**
63+
* @group integration
64+
*/
65+
public function testInformationalResponse()
66+
{
67+
if (!(new ExecutableFinder())->find('curl')) {
68+
$this->markTestSkipped('curl is not installed');
69+
}
70+
71+
if (!($fp = @fsockopen('localhost', 80, $errorCode, $errorMessage, 2))) {
72+
$this->markTestSkipped('FrankenPHP is not running');
73+
}
74+
fclose($fp);
75+
76+
$p = new Process(['curl', '-v', 'http://localhost/early_hints.php']);
77+
$p->run();
78+
$output = $p->getErrorOutput();
79+
80+
$this->assertSame(3, preg_match_all('#Link: </css/style\.css>; rel="preload"; as="style"#', $output));
81+
$this->assertSame(2, preg_match_all('#Link: </js/app\.js>; rel="preload"; as="script"#', $output));
82+
}
5783
}

0 commit comments

Comments
 (0)