CodeIgniter4 icon indicating copy to clipboard operation
CodeIgniter4 copied to clipboard

Bug: CURLRequest keeps the last value provided by headers with the same name

Open NicolaeIotu opened this issue 4 years ago • 1 comments

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

  1. Perform a CURLRequest
  2. Obtain response with multiple values for i.e. 'set-cookie' header: expected - all values, obtained - the last value
  3. Redirect to endpoint while passing all 'set-cookie' headers: expected - redirection OK, obtained - error

Context All

NicolaeIotu avatar Aug 30 '21 08:08 NicolaeIotu

@NicolaeIotu Thank you for reporting. Why don't you send PR? https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/README.md

kenjis avatar Oct 19 '21 13:10 kenjis

I confirmed the bug.

        $client   = \Config\Services::curlrequest();
        $response = $client->request('GET', 'https://github.com/', []);
        dd($response->headers());

Screenshot 2023-11-11 10 01 38

Screenshot 2023-11-11 10 03 29

kenjis avatar Nov 11 '23 01:11 kenjis

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

kenjis avatar Nov 11 '23 01:11 kenjis

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

kenjis avatar Nov 11 '23 11:11 kenjis

I sent PR #8240 to fix this bug. Try if you can.

kenjis avatar Nov 22 '23 01:11 kenjis

This will be fixed in v4.5.0. See #8240

kenjis avatar Dec 20 '23 08:12 kenjis