diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php
index d27d52fdc576..ac09f33f5fec 100644
--- a/app/Views/errors/html/error_exception.php
+++ b/app/Views/errors/html/error_exception.php
@@ -1,4 +1,5 @@
-
+ $value) : ?>
- = esc($header->getName(), 'html') ?> |
- = esc($header->getValueLine(), 'html') ?> |
+ = esc($name, 'html') ?> |
+
+ getValueLine(), 'html');
+ } else {
+ foreach ($value as $i => $header) {
+ echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html');
+ }
+ }
+ ?>
+ |
@@ -316,8 +327,6 @@
headers(); ?>
-
-
Headers
@@ -328,10 +337,20 @@
-
+ $value) : ?>
= esc($name, 'html') ?> |
- = esc($response->getHeaderLine($name), 'html') ?> |
+
+ getHeaderLine($name), 'html');
+ } else {
+ foreach ($value as $i => $header) {
+ echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html');
+ }
+ }
+ ?>
+ |
diff --git a/deptrac.yaml b/deptrac.yaml
index 271378bfde55..4d6cb8912820 100644
--- a/deptrac.yaml
+++ b/deptrac.yaml
@@ -225,6 +225,7 @@ parameters:
# Individual class exemptions
CodeIgniter\Cache\ResponseCache:
- CodeIgniter\HTTP\CLIRequest
+ - CodeIgniter\HTTP\Header
- CodeIgniter\HTTP\IncomingRequest
- CodeIgniter\HTTP\ResponseInterface
CodeIgniter\Entity\Cast\URICast:
diff --git a/system/Cache/ResponseCache.php b/system/Cache/ResponseCache.php
index 79948af10e54..6e8e8b4887b1 100644
--- a/system/Cache/ResponseCache.php
+++ b/system/Cache/ResponseCache.php
@@ -12,6 +12,7 @@
namespace CodeIgniter\Cache;
use CodeIgniter\HTTP\CLIRequest;
+use CodeIgniter\HTTP\Header;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\ResponseInterface;
use Config\Cache as CacheConfig;
@@ -99,8 +100,14 @@ public function make($request, ResponseInterface $response): bool
$headers = [];
- foreach ($response->headers() as $header) {
- $headers[$header->getName()] = $header->getValueLine();
+ foreach ($response->headers() as $name => $value) {
+ if ($value instanceof Header) {
+ $headers[$name] = $value->getValueLine();
+ } else {
+ foreach ($value as $header) {
+ $headers[$name][] = $header->getValueLine();
+ }
+ }
}
return $this->cache->save(
diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php
index b2d54c5d52cc..3f846a36d2e7 100644
--- a/system/Debug/Toolbar.php
+++ b/system/Debug/Toolbar.php
@@ -18,6 +18,7 @@
use CodeIgniter\Format\JSONFormatter;
use CodeIgniter\Format\XMLFormatter;
use CodeIgniter\HTTP\DownloadResponse;
+use CodeIgniter\HTTP\Header;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
@@ -140,8 +141,17 @@ public function run(float $startTime, float $totalTime, RequestInterface $reques
$data['vars']['post'][esc($name)] = is_array($value) ? '' . esc(print_r($value, true)) . '
' : esc($value);
}
- foreach ($request->headers() as $header) {
- $data['vars']['headers'][esc($header->getName())] = esc($header->getValueLine());
+ foreach ($request->headers() as $name => $value) {
+ if ($value instanceof Header) {
+ $data['vars']['headers'][esc($name)] = esc($value->getValueLine());
+ } else {
+ foreach ($value as $i => $header) {
+ $index = $i + 1;
+ $data['vars']['headers'][esc($name)] ??= '';
+ $data['vars']['headers'][esc($name)] .= ' (' . $index . ') '
+ . esc($header->getValueLine());
+ }
+ }
}
foreach ($request->getCookie() as $name => $value) {
@@ -157,8 +167,17 @@ public function run(float $startTime, float $totalTime, RequestInterface $reques
'headers' => [],
];
- foreach ($response->headers() as $header) {
- $data['vars']['response']['headers'][esc($header->getName())] = esc($header->getValueLine());
+ foreach ($response->headers() as $name => $value) {
+ if ($value instanceof Header) {
+ $data['vars']['response']['headers'][esc($name)] = esc($value->getValueLine());
+ } else {
+ foreach ($value as $i => $header) {
+ $index = $i + 1;
+ $data['vars']['response']['headers'][esc($name)] ??= '';
+ $data['vars']['response']['headers'][esc($name)] .= ' (' . $index . ') '
+ . esc($header->getValueLine());
+ }
+ }
}
$data['config'] = Config::display();
diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php
index 9f5cd61ab275..35b62ec3486f 100644
--- a/system/HTTP/CURLRequest.php
+++ b/system/HTTP/CURLRequest.php
@@ -479,10 +479,14 @@ protected function setResponseHeaders(array $headers = [])
{
foreach ($headers as $header) {
if (($pos = strpos($header, ':')) !== false) {
- $title = substr($header, 0, $pos);
- $value = substr($header, $pos + 1);
+ $title = trim(substr($header, 0, $pos));
+ $value = trim(substr($header, $pos + 1));
- $this->response->setHeader($title, $value);
+ if ($this->response instanceof Response) {
+ $this->response->addHeader($title, $value);
+ } else {
+ $this->response->setHeader($title, $value);
+ }
} elseif (strpos($header, 'HTTP') === 0) {
preg_match('#^HTTP\/([12](?:\.[01])?) (\d+) (.+)#', $header, $matches);
diff --git a/system/HTTP/RedirectResponse.php b/system/HTTP/RedirectResponse.php
index d6f234ca9f68..8efd4c2c51ea 100644
--- a/system/HTTP/RedirectResponse.php
+++ b/system/HTTP/RedirectResponse.php
@@ -161,8 +161,14 @@ public function withCookies()
*/
public function withHeaders()
{
- foreach (Services::response()->headers() as $name => $header) {
- $this->setHeader($name, $header->getValue());
+ foreach (Services::response()->headers() as $name => $value) {
+ if ($value instanceof Header) {
+ $this->setHeader($name, $value->getValue());
+ } else {
+ foreach ($value as $header) {
+ $this->addHeader($name, $header->getValue());
+ }
+ }
}
return $this;
diff --git a/system/HTTP/ResponseTrait.php b/system/HTTP/ResponseTrait.php
index 8d8cad504866..e5e006c776aa 100644
--- a/system/HTTP/ResponseTrait.php
+++ b/system/HTTP/ResponseTrait.php
@@ -398,8 +398,22 @@ public function sendHeaders()
header(sprintf('HTTP/%s %s %s', $this->getProtocolVersion(), $this->getStatusCode(), $this->getReasonPhrase()), true, $this->getStatusCode());
// Send all of our headers
- foreach (array_keys($this->headers()) as $name) {
- header($name . ': ' . $this->getHeaderLine($name), false, $this->getStatusCode());
+ foreach ($this->headers() as $name => $value) {
+ if ($value instanceof Header) {
+ header(
+ $name . ': ' . $value->getValueLine(),
+ false,
+ $this->getStatusCode()
+ );
+ } else {
+ foreach ($value as $header) {
+ header(
+ $name . ': ' . $header->getValueLine(),
+ false,
+ $this->getStatusCode()
+ );
+ }
+ }
}
return $this;
diff --git a/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php b/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php
index c9710616e2d2..a7f3268a2b72 100644
--- a/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php
+++ b/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php
@@ -852,6 +852,38 @@ public function testResponseHeadersWithMultipleRequests(): void
$this->assertSame(200, $response->getStatusCode());
}
+ public function testResponseHeadersWithMultipleSetCookies(): void
+ {
+ $request = $this->getRequest([
+ 'base_uri' => 'https://github.com/',
+ ]);
+
+ $output = "HTTP/2 200
+server: GitHub.com
+date: Sat, 11 Nov 2023 02:26:55 GMT
+content-type: text/html; charset=utf-8
+set-cookie: _gh_sess=PlRlha1YumlLhLuo5MuNbIWJRO9RRuR%2FHfYsWRh5B0mkalFIZstlAbTmSstl8q%2FAC57IsWMVuFHWQc6L4qDHQJrwhuYVO5ZaigPCUjAStnhh%2FieZQVqIf92Al7vusuzx2o8XH%2Fv6nd9qzMTAWc2%2FkRsl8jxPQYGNaWeuUBY2w3%2FDORSikN4c0vHOyedhU7Xcv3Ryz5xD3DNxK9R8xKNZ6OSXLJ6bjX8iIT6LxvroVIf2HjvowW9cQsq0kN08mS6KtTnH0mD3ANWqsVVWeMzFNA%3D%3D--Jx830Q9Nmkfz9OGA--kEcPtNphvjNMopYqFDxUbw%3D%3D; Path=/; HttpOnly; Secure; SameSite=Lax
+set-cookie: _octo=GH1.1.599292127.1699669625; Path=/; Domain=github.com; Expires=Mon, 11 Nov 2024 02:27:05 GMT; Secure; SameSite=Lax
+set-cookie: logged_in=no; Path=/; Domain=github.com; Expires=Mon, 11 Nov 2024 02:27:05 GMT; HttpOnly; Secure; SameSite=Lax
+accept-ranges: bytes\x0d\x0a\x0d\x0a";
+ $request->setOutput($output);
+
+ $response = $request->get('/');
+
+ $setCookieHeaders = $response->header('set-cookie');
+
+ $this->assertCount(3, $setCookieHeaders);
+ $this->assertSame(
+ 'logged_in=no; Path=/; Domain=github.com; Expires=Mon, 11 Nov 2024 02:27:05 GMT; HttpOnly; Secure; SameSite=Lax',
+ $setCookieHeaders[2]->getValue()
+ );
+
+ $this->assertSame(
+ '_octo=GH1.1.599292127.1699669625; Path=/; Domain=github.com; Expires=Mon, 11 Nov 2024 02:27:05 GMT; Secure; SameSite=Lax',
+ $setCookieHeaders[1]->getValueLine()
+ );
+ }
+
public function testSplitResponse(): void
{
$request = $this->getRequest([