Bug: CURLRequest keeps the last value provided by headers with the same name
It is not possible to get and forward all headers having multiple values of a response when performing a CURLRequest. My experience was with 'set-cookie' header, but after investigating it was pretty obvious that the CURLRequest (and probably other involved or related classes) will only retain the last value of headers with the same name and in case of redirection trigger ErrorException: Header may not contain more than a single header, new line detected (SYSTEMPATH/HTTP/ResponseTrait.php at line 471).
The following fixed the issue for me:
System\HTTP\CURLRequest
protected function parseOptions(array $options)
{
if (array_key_exists('baseURI', $options)) {
$this->baseURI = $this->baseURI->setURI($options['baseURI']);
unset($options['baseURI']);
}
if (array_key_exists('headers', $options) && is_array($options['headers'])) {
foreach ($options['headers'] as $name => $value) {
// my adjustment
if($this->hasHeader($name)) {
$this->appendHeader($name, $value);
} else {
$this->setHeader($name, $value);
}
// end my adjustment
}
unset($options['headers']);
}
...
System\HTTP\ResponseTrait
public function sendHeaders()
{
// Have the headers already been sent?
if ($this->pretend || headers_sent()) {
return $this;
}
// Per spec, MUST be sent with each request, if possible.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
if (! isset($this->headers['Date']) && PHP_SAPI !== 'cli-server') {
$this->setDate(DateTime::createFromFormat('U', (string) time()));
}
// HTTP Status
header(sprintf('HTTP/%s %s %s', $this->getProtocolVersion(), $this->getStatusCode(), $this->getReason()), true, $this->getStatusCode());
// Send all of our headers
foreach (array_keys($this->getHeaders()) as $name) {
// my adjustment
$headerValue = $this->getHeader($name)->getValue();
if(is_array($headerValue)) {
foreach($headerValue as $h_value) {
header($name . ': ' . $h_value, false, $this->getStatusCode());
}
} else {
header($name . ': ' . $this->getHeaderLine($name), false, $this->getStatusCode());
}
// end my adjustment
}
return $this;
}
CodeIgniter 4 version 4.1.3
Affected module(s) System\HTTP\CURLRequest System\HTTP\ResponseTrait probably some of the other classes as well
Expected behavior, and steps to reproduce if appropriate
- Perform a CURLRequest
- Obtain response with multiple values for i.e. 'set-cookie' header: expected - all values, obtained - the last value
- Redirect to endpoint while passing all 'set-cookie' headers: expected - redirection OK, obtained - error
Context All
@NicolaeIotu Thank you for reporting. Why don't you send PR? https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/README.md
I confirmed the bug.
$client = \Config\Services::curlrequest();
$response = $client->request('GET', 'https://github.com/', []);
dd($response->headers());
The Set-Cookie HTTP response header is used to send a cookie from the server to the user agent, so that the user agent can send it back to the server later. To send multiple cookies, multiple Set-Cookie headers should be sent in the same response. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
I found this comment. But "return an array of header objects" is not yet implemented. https://github.com/codeigniter4/CodeIgniter4/blob/0ab4d674a0364d5a4837c9ec05f544f342b332ba/system/HTTP/MessageTrait.php#L120-L121
I sent PR #8240 to fix this bug. Try if you can.
This will be fixed in v4.5.0. See #8240