diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index b9ba20ba9926a..6464a2f1f31e5 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -110,6 +110,16 @@ jobs: KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1 KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' KAFKA_ADVERTISED_PORT: 9092 + frankenphp: + image: dunglas/frankenphp:1.1.0 + ports: + - 80:80 + volumes: + - ${{ github.workspace }}:/symfony + env: + SERVER_NAME: 'http://localhost' + CADDY_SERVER_EXTRA_DIRECTIVES: | + root * /symfony/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/ steps: - name: Checkout diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index d67c8f7264432..a43e7a9ac21e3 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -355,23 +355,21 @@ public function sendHeaders(/* int $statusCode = null */): static $replace = false; // As recommended by RFC 8297, PHP automatically copies headers from previous 103 responses, we need to deal with that if headers changed - if (103 === $statusCode) { - $previousValues = $this->sentHeaders[$name] ?? null; - if ($previousValues === $values) { - // Header already sent in a previous response, it will be automatically copied in this response by PHP - continue; - } + $previousValues = $this->sentHeaders[$name] ?? null; + if ($previousValues === $values) { + // Header already sent in a previous response, it will be automatically copied in this response by PHP + continue; + } - $replace = 0 === strcasecmp($name, 'Content-Type'); + $replace = 0 === strcasecmp($name, 'Content-Type'); - if (null !== $previousValues && array_diff($previousValues, $values)) { - header_remove($name); - $previousValues = null; - } - - $newValues = null === $previousValues ? $values : array_diff($values, $previousValues); + if (null !== $previousValues && array_diff($previousValues, $values)) { + header_remove($name); + $previousValues = null; } + $newValues = null === $previousValues ? $values : array_diff($values, $previousValues); + foreach ($newValues as $value) { header($name.': '.$value, $replace, $this->statusCode); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/early_hints.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/early_hints.php new file mode 100644 index 0000000000000..90294d9ae2d0e --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/early_hints.php @@ -0,0 +1,31 @@ +headers->set('Link', '; rel="preload"; as="style"'); +$r->sendHeaders(103); + +$r->headers->set('Link', '; rel="preload"; as="script"', false); +$r->sendHeaders(103); + +$r->setContent('Hello, Early Hints'); +$r->send(); diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php index ccda147df6a57..1b3566a2c4860 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\HttpFoundation\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\ExecutableFinder; +use Symfony\Component\Process\Process; class ResponseFunctionalTest extends TestCase { @@ -51,7 +53,31 @@ public function testCookie($fixture) public static function provideCookie() { foreach (glob(__DIR__.'/Fixtures/response-functional/*.php') as $file) { - yield [pathinfo($file, \PATHINFO_FILENAME)]; + if (str_contains($file, 'cookie')) { + yield [pathinfo($file, \PATHINFO_FILENAME)]; + } } } + + /** + * @group integration + */ + public function testInformationalResponse() + { + if (!(new ExecutableFinder())->find('curl')) { + $this->markTestSkipped('curl is not installed'); + } + + if (!($fp = @fsockopen('localhost', 80, $errorCode, $errorMessage, 2))) { + $this->markTestSkipped('FrankenPHP is not running'); + } + fclose($fp); + + $p = new Process(['curl', '-v', 'http://localhost/early_hints.php']); + $p->run(); + $output = $p->getErrorOutput(); + + $this->assertSame(3, preg_match_all('#Link: ; rel="preload"; as="style"#', $output)); + $this->assertSame(2, preg_match_all('#Link: ; rel="preload"; as="script"#', $output)); + } }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: