diff --git a/src/Symfony/Component/HttpFoundation/Cookie.php b/src/Symfony/Component/HttpFoundation/Cookie.php index 4519a6adaeda5..d202dc6c1efb5 100644 --- a/src/Symfony/Component/HttpFoundation/Cookie.php +++ b/src/Symfony/Component/HttpFoundation/Cookie.php @@ -145,12 +145,12 @@ public function __toString() $str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'='; if ('' === (string) $this->getValue()) { - $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001'; + $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0'; } else { $str .= $this->isRaw() ? $this->getValue() : rawurlencode($this->getValue()); if (0 !== $this->getExpiresTime()) { - $str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()).'; max-age='.$this->getMaxAge(); + $str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()).'; Max-Age='.$this->getMaxAge(); } } @@ -224,7 +224,9 @@ public function getExpiresTime() */ public function getMaxAge() { - return 0 !== $this->expire ? $this->expire - time() : 0; + $maxAge = $this->expire - time(); + + return 0 >= $maxAge ? 0 : $maxAge; } /** diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 6f8a6239593b1..f3e45b57cbc7d 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -327,7 +327,7 @@ public function sendHeaders() } // headers - foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) { + foreach ($this->headers->allPreserveCase() as $name => $values) { foreach ($values as $value) { header($name.': '.$value, false, $this->statusCode); } @@ -336,15 +336,6 @@ public function sendHeaders() // status header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); - // cookies - foreach ($this->headers->getCookies() as $cookie) { - if ($cookie->isRaw()) { - setrawcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); - } else { - setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); - } - } - return $this; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php b/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php index 070b7dd4290e6..a47b71e1d4257 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php @@ -162,13 +162,13 @@ public function testCookieIsCleared() public function testToString() { $cookie = new Cookie('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); - $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; max-age='.($expire - time()).'; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie'); + $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie'); $cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); - $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; max-age='.($expire - time()).'; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)'); + $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)'); $cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com'); - $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; max-age='.($expire - time()).'; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL'); + $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL'); $cookie = new Cookie('foo', 'bar', 0, '/', ''); $this->assertEquals('foo=bar; path=/; httponly', (string) $cookie); @@ -194,7 +194,7 @@ public function testGetMaxAge() $this->assertEquals($expire - time(), $cookie->getMaxAge()); $cookie = new Cookie('foo', 'bar', $expire = time() - 100); - $this->assertEquals($expire - time(), $cookie->getMaxAge()); + $this->assertEquals(0, $cookie->getMaxAge()); } public function testFromString() diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/common.inc b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/common.inc new file mode 100644 index 0000000000000..ba101d357852d --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/common.inc @@ -0,0 +1,39 @@ +headers->set('Date', 'Sat, 12 Nov 1955 20:04:00 GMT'); + +return $r; diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.expected new file mode 100644 index 0000000000000..bdb9d023f8f3d --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.expected @@ -0,0 +1,11 @@ + +Warning: Expiry date cannot have a year greater than 9999 in %scookie_max_age.php on line 10 + +Array +( + [0] => Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: no-cache, private + [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT + [3] => Set-Cookie: foo=bar; expires=Sat, 01-Jan-10000 02:46:40 GMT; Max-Age=%d; path=/ +) +shutdown diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.php new file mode 100644 index 0000000000000..8775a5cceeb88 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_max_age.php @@ -0,0 +1,10 @@ +headers->setCookie(new Cookie('foo', 'bar', 253402310800, '', null, false, false)); +$r->sendHeaders(); + +setcookie('foo2', 'bar', 253402310800, '/'); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected new file mode 100644 index 0000000000000..0c097972e78e2 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected @@ -0,0 +1,10 @@ + +Array +( + [0] => Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: no-cache, private + [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT + [3] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/ + [4] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/ +) +shutdown diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php new file mode 100644 index 0000000000000..2ca5b59f1a3e5 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php @@ -0,0 +1,12 @@ +headers->setCookie(new Cookie($str, $str, 0, '/', null, false, false, true)); +$r->sendHeaders(); + +setrawcookie($str, $str, 0, '/', null, false, false); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected new file mode 100644 index 0000000000000..cbde2cbfe13f3 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected @@ -0,0 +1,9 @@ + +Array +( + [0] => Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: no-cache, private + [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT + [3] => Set-Cookie: CookieSamesiteLaxTest=LaxValue; path=/; httponly; samesite=lax +) +shutdown diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php new file mode 100644 index 0000000000000..9a476f1d23fab --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php @@ -0,0 +1,8 @@ +headers->setCookie(new Cookie('CookieSamesiteLaxTest', 'LaxValue', 0, '/', null, false, true, false, Cookie::SAMESITE_LAX)); +$r->sendHeaders(); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected new file mode 100644 index 0000000000000..adc491fd2bc51 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected @@ -0,0 +1,9 @@ + +Array +( + [0] => Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: no-cache, private + [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT + [3] => Set-Cookie: CookieSamesiteStrictTest=StrictValue; path=/; httponly; samesite=strict +) +shutdown diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php new file mode 100644 index 0000000000000..3bcb41f8f059e --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php @@ -0,0 +1,8 @@ +headers->setCookie(new Cookie('CookieSamesiteStrictTest', 'StrictValue', 0, '/', null, false, true, false, Cookie::SAMESITE_STRICT)); +$r->sendHeaders(); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected new file mode 100644 index 0000000000000..4e9c4c075f5ed --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected @@ -0,0 +1,10 @@ + +Array +( + [0] => Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: no-cache, private + [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT + [3] => Set-Cookie: %3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/ + [4] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/ +) +shutdown diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.php new file mode 100644 index 0000000000000..05b9af30d58f2 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.php @@ -0,0 +1,12 @@ +headers->setCookie(new Cookie($str, $str, 0, '', null, false, false)); +$r->sendHeaders(); + +setcookie($str, $str, 0, '/'); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected new file mode 100644 index 0000000000000..2b560f0bd5689 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected @@ -0,0 +1,6 @@ +The cookie name "Hello + world" contains invalid characters. +Array +( + [0] => Content-Type: text/plain; charset=utf-8 +) +shutdown diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php new file mode 100644 index 0000000000000..3fe1571845628 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php @@ -0,0 +1,11 @@ +headers->setCookie(new Cookie('Hello + world', 'hodor')); +} catch (\InvalidArgumentException $e) { + echo $e->getMessage(); +} diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php new file mode 100644 index 0000000000000..22f25e978e5a2 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseFunctionalTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use PHPUnit\Framework\TestCase; + +/** + * @requires PHP 7.0 + */ +class ResponseFunctionalTest extends TestCase +{ + private static $server; + + public static function setUpBeforeClass() + { + $spec = array( + 1 => array('file', '/dev/null', 'w'), + 2 => array('file', '/dev/null', 'w'), + ); + if (!self::$server = @proc_open('exec php -S localhost:8054', $spec, $pipes, __DIR__.'/Fixtures/response-functional')) { + self::markTestSkipped('PHP server unable to start.'); + } + sleep(1); + } + + public static function tearDownAfterClass() + { + if (self::$server) { + proc_terminate(self::$server); + proc_close(self::$server); + } + } + + /** + * @dataProvider provideCookie + */ + public function testCookie($fixture) + { + $result = file_get_contents(sprintf('http://localhost:8054/%s.php', $fixture)); + $this->assertStringMatchesFormatFile(__DIR__.sprintf('/Fixtures/response-functional/%s.expected', $fixture), $result); + } + + public function provideCookie() + { + foreach (glob(__DIR__.'/Fixtures/response-functional/*.php') as $file) { + yield array(pathinfo($file, PATHINFO_FILENAME)); + } + } +} diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php index ce8553590dcdb..7b5e720afdb61 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php @@ -116,7 +116,7 @@ public function testToStringIncludesCookieHeaders() $bag->clearCookie('foo'); - $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001; path=/; httponly', $bag); + $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; httponly', $bag); } public function testClearCookieSecureNotHttpOnly() @@ -125,7 +125,7 @@ public function testClearCookieSecureNotHttpOnly() $bag->clearCookie('foo', '/', null, true, false); - $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001; path=/; secure', $bag); + $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure', $bag); } public function testReplace() diff --git a/src/Symfony/Component/HttpKernel/Tests/ClientTest.php b/src/Symfony/Component/HttpKernel/Tests/ClientTest.php index 051d5d47c0265..b774d8ec7ae1c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/ClientTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/ClientTest.php @@ -60,22 +60,17 @@ public function testFilterResponseConvertsCookies() $m = $r->getMethod('filterResponse'); $m->setAccessible(true); - $expected = array( - 'foo=bar; expires=Sun, 15-Feb-2009 20:00:00 GMT; max-age='.(strtotime('Sun, 15-Feb-2009 20:00:00 GMT') - time()).'; path=/foo; domain=http://example.com; secure; httponly', - 'foo1=bar1; expires=Sun, 15-Feb-2009 20:00:00 GMT; max-age='.(strtotime('Sun, 15-Feb-2009 20:00:00 GMT') - time()).'; path=/foo; domain=http://example.com; secure; httponly', - ); - $response = new Response(); - $response->headers->setCookie(new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); + $response->headers->setCookie($cookie1 = new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); $domResponse = $m->invoke($client, $response); - $this->assertEquals($expected[0], $domResponse->getHeader('Set-Cookie')); + $this->assertSame((string) $cookie1, $domResponse->getHeader('Set-Cookie')); $response = new Response(); - $response->headers->setCookie(new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); - $response->headers->setCookie(new Cookie('foo1', 'bar1', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); + $response->headers->setCookie($cookie1 = new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); + $response->headers->setCookie($cookie2 = new Cookie('foo1', 'bar1', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); $domResponse = $m->invoke($client, $response); - $this->assertEquals($expected[0], $domResponse->getHeader('Set-Cookie')); - $this->assertEquals($expected, $domResponse->getHeader('Set-Cookie', false)); + $this->assertSame((string) $cookie1, $domResponse->getHeader('Set-Cookie')); + $this->assertSame(array((string) $cookie1, (string) $cookie2), $domResponse->getHeader('Set-Cookie', false)); } public function testFilterResponseSupportsStreamedResponses()
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: