Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getCookie raw vs decoded name/value #588

Open
bkdotcom opened this issue Jun 6, 2019 · 5 comments
Open

getCookie raw vs decoded name/value #588

bkdotcom opened this issue Jun 6, 2019 · 5 comments

Comments

@bkdotcom
Copy link

bkdotcom commented Jun 6, 2019

What is the expected behavior?

getCookie() : expect arg to accept a decoded name
expect the return value to be decoded

getCookies() expect decoded names/values

responseCookies public property : expect decoded names/values

What is the actual behavior?

must pass an encoded name to getCookie() and it returns a raw/encoded value

What steps will reproduce the problem?

response headers include

Set-Cookie: dingus=foo%3Bbar; path=/
Set-Cookie: a%3Bb=foo%3Bbar; path=/

Code:

$curl->getCookie('dingus') // returns "foo%3Bbar"   I would expect "foo;bar"
$curl->getCookie('a;b'); // returns null, I would expect "foo;bar"
$curl->getCookie('a%3Bb'); // returns "foo%3Bbar"  -> ugh

likewise, I would expect getCookies (and public responseCookies property) to return array of decoded keys/values

feature or bug?

@zachborboa
Copy link
Member

Hi, I believe the current behavior is correct. I've tested in 2 other common libraries:

Set cookies script

// Set-Cookie: dingus=foo%3Bbar; path=/
$name = 'dingus';
$value = urldecode('foo%3Bbar');
setcookie($name, $value, $expires = 0, $path = "/");

// Set-Cookie: a%3Bb=foo%3Bbar; path=/
$name = 'a%3Bb';
$value = urldecode('foo%3Bbar');
setcookie($name, $value, $expires = 0, $path = "/");

Run server

$ php -S 127.0.0.1:8000 setcookies.php

Test in php using https://github.com/rmccue/Requests

require __DIR__ . '/vendor/autoload.php';

Requests::register_autoloader();

$response = Requests::get('http://127.0.0.1:8000/');
var_dump((string)$response->cookies['dingus']); // string(9) "foo%3Bbar"
var_dump((string)$response->cookies['a;b']); // string(0) ""
var_dump((string)$response->cookies['a%3Bb']); // string(9) "foo%3Bbar"

Test in python using https://github.com/kennethreitz/requests

[nav] In [1]: import requests                                                                                                     

[ins] In [2]: r = requests.get('http://127.0.0.1:8000/')                                                                          

[ins] In [3]: r.cookies.get('dingus')                                                                                             
Out[3]: 'foo%3Bbar'

[ins] In [4]: r.cookies.get('a;b')                                                                                                

[ins] In [5]: r.cookies.get('a%3Bb')                                                                                              
Out[5]: 'foo%3Bbar'

@bkdotcom
Copy link
Author

bkdotcom commented Jun 10, 2019

Thank you for your prompt response..

https://github.com/kennethreitz/requests has the motto "http for humans"..

I've opened psf/requests#5116 inquiring as to why r.cookies.get() 's return value isn't for humans

even guzzle is basing its behavior on the python library guzzle/guzzle#99

\Curl\Curl::setCookie($name, $value) takes decoded name/value and handles the encoding for me
getCookie should do the opposite

the encoding is a transport layer detail that should be handled by the library (just as it mostly is with PHP itself).

php's setcookie transparently encodes the value (but not the name)
php's $_COOKIE superglobal is "correct"
var_dump($_COOKIE);

array (size=2)
  'dingus' => string 'foo;bar' (length=7)
  'a;b' => string 'foo;bar' (length=7)

If I want the raw cookie header in php :
var_dump($_SERVER["HTTP_COOKIE"]);

string 'dingus=foo%3Bbar; a%3Bb=foo%3Bbar' (length=33)

@zachborboa
Copy link
Member

To be able to access cookie values using the decoded cookie name (e.g. $curl->getCookie('a;b')) and access decoded cookie values, the following change appears to work.

Note that urldecode is used instead of rawurldecode because rawurldecode doesn't decode plus symbols into spaces whereas urldecode does.

diff --git a/src/Curl/Curl.php b/src/Curl/Curl.php
index fd4836f..967127a 100644
--- a/src/Curl/Curl.php
+++ b/src/Curl/Curl.php
@@ -1784,7 +1784,7 @@ class Curl
 function createHeaderCallback($header_callback_data) {
     return function ($ch, $header) use ($header_callback_data) {
         if (preg_match('/^Set-Cookie:\s*([^=]+)=([^;]+)/mi', $header, $cookie) === 1) {
-            $header_callback_data->responseCookies[$cookie[1]] = trim($cookie[2], " \n\r\t\0\x0B");
+            $header_callback_data->responseCookies[urldecode($cookie[1])] = urldecode(trim($cookie[2], " \n\r\t\0\x0B"));
         }
         $header_callback_data->rawResponseHeaders .= $header;
         return strlen($header);

@zachborboa
Copy link
Member

Perhaps fallbacks could be added so that $curl->getCookie('a;b'); and $response->cookies['a;b'] work as expected:

// Fix for $curl->getCookie('a;b');.
public function getCookie($key)
{
    if (isset($this->responseCookies[$key])) {
        return $this->responseCookies[$key];
    } elseif (isset($this->responseCookies[urlencode($key)])) {
        return $this->responseCookies[urlencode($key)];
    } else {
        return null;
    }
}
// Fix for $response->cookies['a;b'];
// would need to be done in CaseInsensitiveArray::offsetGet().
public function offsetGet($offset)
{
    $offsetlower = strtolower($offset);
    if (isset($this->data[$offsetlower])) {
        return $this->data[$offsetlower];
    } elseif (isset($this->data[urlencode($offsetlower)])) {
        return $this->data[urlencode($offsetlower)];
    } else {
        return null;
    }
}

@zachborboa
Copy link
Member

I think what would help move this ticket forward are real-world examples for each of the following cases:

  • cookie name is encoded, cookie value is encoded (Set-Cookie: a%3Bb=foo%3Bbar;)
  • cookie name is not encoded, cookie value is encoded (Set-Cookie: ab=foo%3Bbar;)
  • cookie name is encoded, cookie value is not encoded (Set-Cookie: a%3Bb=foobar;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants