Skip to content

Commit

Permalink
[HttpFoundation] Add support for the 103 status code (Early Hints)
Browse files Browse the repository at this point in the history
  • Loading branch information
dunglas committed Nov 6, 2022
1 parent 4d4c411 commit 85bb5ca
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 8 deletions.
7 changes: 7 additions & 0 deletions UPGRADE-6.3.md
@@ -0,0 +1,7 @@
UPGRADE FROM 6.2 to 6.3
=======================

HttpFoundation
--------------

* Deprecate calling `Response::sendHeaders()` without any arguments
5 changes: 5 additions & 0 deletions src/Symfony/Component/HttpFoundation/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========

6.3
---

* The `Response::sendHeaders()` method now takes an HTTP status code as parameter, allowing to send informational responses such as Early Hints responses (103 status code)

6.2
---

Expand Down
29 changes: 27 additions & 2 deletions src/Symfony/Component/HttpFoundation/Response.php
Expand Up @@ -326,10 +326,20 @@ public function prepare(Request $request): static
/**
* Sends HTTP headers.
*
* @param null|positive-int $statusCode The status code to use. Override the statusCode property if set and not null.
*
* @return $this
*/
public function sendHeaders(): static
{
if (1 > \func_num_args()) {
trigger_deprecation('symfony/http-foundation', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__);

$statusCode = null;
} else {
$statusCode = func_get_arg(0) ?: null;
}

// headers have already been sent by the developer
if (headers_sent()) {
return $this;
Expand All @@ -348,8 +358,23 @@ public function sendHeaders(): static
header('Set-Cookie: '.$cookie, false, $this->statusCode);
}

if ($statusCode) {
if (\function_exists('headers_send')) {
headers_send($statusCode);

return $this;
}

if ($statusCode >= 100 && $statusCode < 200) {
// skip informational responses if not supported by the SAPI
return $this;
}
} else {
$statusCode = $this->statusCode;
}

// status
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
header(sprintf('HTTP/%s %s %s', $this->version, $statusCode, $this->statusText), true, $statusCode);

return $this;
}
Expand All @@ -373,7 +398,7 @@ public function sendContent(): static
*/
public function send(): static
{
$this->sendHeaders();
$this->sendHeaders(null);
$this->sendContent();

if (\function_exists('fastcgi_finish_request')) {
Expand Down
4 changes: 3 additions & 1 deletion src/Symfony/Component/HttpFoundation/StreamedResponse.php
Expand Up @@ -59,6 +59,8 @@ public function setCallback(callable $callback): static
/**
* This method only sends the headers once.
*
* @param null|positive-int $statusCode The status code to use. Override the statusCode property if set and not null.
*
* @return $this
*/
public function sendHeaders(): static
Expand All @@ -69,7 +71,7 @@ public function sendHeaders(): static

$this->headersSent = true;

return parent::sendHeaders();
return parent::sendHeaders(...\func_get_args());
}

/**
Expand Down
Expand Up @@ -57,4 +57,4 @@ public function handle(Request $request, int $type = self::MAIN_REQUEST, bool $c

$listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $r));

$r->sendHeaders();
$r->sendHeaders(null);
13 changes: 12 additions & 1 deletion src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php
Expand Up @@ -38,7 +38,7 @@ public function testClone()
public function testSendHeaders()
{
$response = new Response();
$headers = $response->sendHeaders();
$headers = $response->sendHeaders(null);
$this->assertObjectHasAttribute('headers', $headers);
$this->assertObjectHasAttribute('content', $headers);
$this->assertObjectHasAttribute('version', $headers);
Expand All @@ -47,6 +47,17 @@ public function testSendHeaders()
$this->assertObjectHasAttribute('charset', $headers);
}

public function testSendInformationalResponse()
{
$response = new Response();
$response->sendHeaders(103);

// Informational responses must not override the main status code
$this->assertSame(200, $response->getStatusCode());

$response->sendHeaders(null);
}

public function testSend()
{
$response = new Response();
Expand Down
Expand Up @@ -108,8 +108,8 @@ public function testReturnThis()
$this->assertInstanceOf(StreamedResponse::class, $response->sendContent());

$response = new StreamedResponse(function () {});
$this->assertInstanceOf(StreamedResponse::class, $response->sendHeaders());
$this->assertInstanceOf(StreamedResponse::class, $response->sendHeaders());
$this->assertInstanceOf(StreamedResponse::class, $response->sendHeaders(null));
$this->assertInstanceOf(StreamedResponse::class, $response->sendHeaders(null));
}

public function testSetNotModified()
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/HttpKernel/HttpKernel.php
Expand Up @@ -118,7 +118,7 @@ public function terminateWithException(\Throwable $exception, Request $request =
}
}

$response->sendHeaders();
$response->sendHeaders(null);
$response->sendContent();

$this->terminate($request, $response);
Expand Down

0 comments on commit 85bb5ca

Please sign in to comment.