merged branch jakzal/SetCookieWithMultipleCookiesBugFix (PR #3823)

Commits
-------

7f92833 [BrowserKit] Fixed cs.
df3da28 [BrowserKit] Using assertNull instead of assertEquals.
87890d3 [BrowserKit] Fixed CookieJar issue being unable to parse multiple cookies from Set-Cookie.

Discussion
----------

[BrowserKit] Fixed CookieJar being unable to parse multiple cookies

Fix proposition for #3109

My fix splits value of *Set-Cookie* header by comma. Than it checks each extracted part if it starts with a cookie-name (token). If check is positive cookie is added to the list. Otherwise it's appended to the previous value. First element is always added to the list.

[rfc6265](http://tools.ietf.org/html/rfc6265) defines cookie-name with token:

    cookie-name = token
    token = <token, defined in [RFC2616], Section 2.2>

token is defined in [rfc2616](http://tools.ietf.org/html/rfc2616#section-2.2) as follows:

    token = 1*<any CHAR except CTLs or separators>
    CHAR = <any US-ASCII character (octets 0 - 127)>
    separators = "(" | ")" | "<" | ">" | "@"
                  | "," | ";" | ":" | "\" | <">
                  | "/" | "[" | "]" | "?" | "="
                  | "{" | "}" | SP | HT

That means cookie-name can be built out of following set of characters: *! # $ % & ' * + - . ^ _ ` | ~ 0-9 A-Z a-z*

Bug fix: yes
Feature addition: no
Backwards compatibility break: no
Symfony2 tests pass: yes
This commit is contained in:
Fabien Potencier 2012-04-07 21:57:48 +02:00
commit dee79e910d
2 changed files with 40 additions and 0 deletions

View File

@ -96,7 +96,19 @@ class CookieJar
*/
public function updateFromResponse(Response $response, $uri = null)
{
$cookies = array();
foreach ($response->getHeader('Set-Cookie', false) as $cookie) {
foreach (explode(',', $cookie) as $i => $part) {
if (0 === $i || preg_match('/^(?P<token>\s*[0-9A-Za-z!#\$%\&\'\*\+\-\.^_`\|~]+)=/', $part)) {
$cookies[] = ltrim($part);
} else {
$cookies[count($cookies) - 1] .= ','.$part;
}
}
}
foreach ($cookies as $cookie) {
$this->set(Cookie::fromString($cookie, $uri));
}
}

View File

@ -66,10 +66,38 @@ class CookieJarTest extends \PHPUnit_Framework_TestCase
$cookieJar->set(new Cookie('bar', 'bar'));
$cookieJar->updateFromResponse($response);
$this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $cookieJar->get('foo'));
$this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $cookieJar->get('bar'));
$this->assertEquals('foo', $cookieJar->get('foo')->getValue(), '->updateFromResponse() updates cookies from a Response objects');
$this->assertEquals('bar', $cookieJar->get('bar')->getValue(), '->updateFromResponse() keeps existing cookies');
}
public function testUpdateFromResponseWithMultipleCookies()
{
$timestamp = time() + 3600;
$date = gmdate('D, d M Y H:i:s \G\M\T', $timestamp);
$response = new Response('', 200, array(
'Set-Cookie' => sprintf('foo=foo; expires=%s; domain=.symfony.com; path=/, bar=bar; domain=.blog.symfony.com, PHPSESSID=id; expires=%s', $date, $date)
));
$cookieJar = new CookieJar();
$cookieJar->updateFromResponse($response);
$fooCookie = $cookieJar->get('foo', '/', '.symfony.com');
$barCookie = $cookieJar->get('bar', '/', '.blog.symfony.com');
$phpCookie = $cookieJar->get('PHPSESSID');
$this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $fooCookie);
$this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $barCookie);
$this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $phpCookie);
$this->assertEquals('foo', $fooCookie->getValue());
$this->assertEquals('bar', $barCookie->getValue());
$this->assertEquals('id', $phpCookie->getValue());
$this->assertEquals($timestamp, $fooCookie->getExpiresTime());
$this->assertNull($barCookie->getExpiresTime());
$this->assertEquals($timestamp, $phpCookie->getExpiresTime());
}
/**
* @dataProvider provideAllValuesValues
*/