1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\HttpFoundation\Tests; 13 14use Symfony\Component\HttpFoundation\ResponseHeaderBag; 15use Symfony\Component\HttpFoundation\Cookie; 16 17/** 18 * @group time-sensitive 19 */ 20class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase 21{ 22 /** 23 * @dataProvider provideAllPreserveCase 24 */ 25 public function testAllPreserveCase($headers, $expected) 26 { 27 $bag = new ResponseHeaderBag($headers); 28 29 $this->assertEquals($expected, $bag->allPreserveCase(), '->allPreserveCase() gets all input keys in original case'); 30 } 31 32 public function provideAllPreserveCase() 33 { 34 return array( 35 array( 36 array('fOo' => 'BAR'), 37 array('fOo' => array('BAR'), 'Cache-Control' => array('no-cache')), 38 ), 39 array( 40 array('ETag' => 'xyzzy'), 41 array('ETag' => array('xyzzy'), 'Cache-Control' => array('private, must-revalidate')), 42 ), 43 array( 44 array('Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ=='), 45 array('Content-MD5' => array('Q2hlY2sgSW50ZWdyaXR5IQ=='), 'Cache-Control' => array('no-cache')), 46 ), 47 array( 48 array('P3P' => 'CP="CAO PSA OUR"'), 49 array('P3P' => array('CP="CAO PSA OUR"'), 'Cache-Control' => array('no-cache')), 50 ), 51 array( 52 array('WWW-Authenticate' => 'Basic realm="WallyWorld"'), 53 array('WWW-Authenticate' => array('Basic realm="WallyWorld"'), 'Cache-Control' => array('no-cache')), 54 ), 55 array( 56 array('X-UA-Compatible' => 'IE=edge,chrome=1'), 57 array('X-UA-Compatible' => array('IE=edge,chrome=1'), 'Cache-Control' => array('no-cache')), 58 ), 59 array( 60 array('X-XSS-Protection' => '1; mode=block'), 61 array('X-XSS-Protection' => array('1; mode=block'), 'Cache-Control' => array('no-cache')), 62 ), 63 ); 64 } 65 66 public function testCacheControlHeader() 67 { 68 $bag = new ResponseHeaderBag(array()); 69 $this->assertEquals('no-cache', $bag->get('Cache-Control')); 70 $this->assertTrue($bag->hasCacheControlDirective('no-cache')); 71 72 $bag = new ResponseHeaderBag(array('Cache-Control' => 'public')); 73 $this->assertEquals('public', $bag->get('Cache-Control')); 74 $this->assertTrue($bag->hasCacheControlDirective('public')); 75 76 $bag = new ResponseHeaderBag(array('ETag' => 'abcde')); 77 $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); 78 $this->assertTrue($bag->hasCacheControlDirective('private')); 79 $this->assertTrue($bag->hasCacheControlDirective('must-revalidate')); 80 $this->assertFalse($bag->hasCacheControlDirective('max-age')); 81 82 $bag = new ResponseHeaderBag(array('Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT')); 83 $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); 84 85 $bag = new ResponseHeaderBag(array( 86 'Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT', 87 'Cache-Control' => 'max-age=3600', 88 )); 89 $this->assertEquals('max-age=3600, private', $bag->get('Cache-Control')); 90 91 $bag = new ResponseHeaderBag(array('Last-Modified' => 'abcde')); 92 $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); 93 94 $bag = new ResponseHeaderBag(array('Etag' => 'abcde', 'Last-Modified' => 'abcde')); 95 $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); 96 97 $bag = new ResponseHeaderBag(array('cache-control' => 'max-age=100')); 98 $this->assertEquals('max-age=100, private', $bag->get('Cache-Control')); 99 100 $bag = new ResponseHeaderBag(array('cache-control' => 's-maxage=100')); 101 $this->assertEquals('s-maxage=100', $bag->get('Cache-Control')); 102 103 $bag = new ResponseHeaderBag(array('cache-control' => 'private, max-age=100')); 104 $this->assertEquals('max-age=100, private', $bag->get('Cache-Control')); 105 106 $bag = new ResponseHeaderBag(array('cache-control' => 'public, max-age=100')); 107 $this->assertEquals('max-age=100, public', $bag->get('Cache-Control')); 108 109 $bag = new ResponseHeaderBag(); 110 $bag->set('Last-Modified', 'abcde'); 111 $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); 112 } 113 114 public function testToStringIncludesCookieHeaders() 115 { 116 $bag = new ResponseHeaderBag(array()); 117 $bag->setCookie(new Cookie('foo', 'bar')); 118 119 $this->assertContains('Set-Cookie: foo=bar; path=/; httponly', explode("\r\n", $bag->__toString())); 120 121 $bag->clearCookie('foo'); 122 123 $this->assertRegExp('#^Set-Cookie: foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; httponly#m', $bag->__toString()); 124 } 125 126 public function testClearCookieSecureNotHttpOnly() 127 { 128 $bag = new ResponseHeaderBag(array()); 129 130 $bag->clearCookie('foo', '/', null, true, false); 131 132 $this->assertRegExp('#^Set-Cookie: foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; secure#m', $bag->__toString()); 133 } 134 135 public function testReplace() 136 { 137 $bag = new ResponseHeaderBag(array()); 138 $this->assertEquals('no-cache', $bag->get('Cache-Control')); 139 $this->assertTrue($bag->hasCacheControlDirective('no-cache')); 140 141 $bag->replace(array('Cache-Control' => 'public')); 142 $this->assertEquals('public', $bag->get('Cache-Control')); 143 $this->assertTrue($bag->hasCacheControlDirective('public')); 144 } 145 146 public function testReplaceWithRemove() 147 { 148 $bag = new ResponseHeaderBag(array()); 149 $this->assertEquals('no-cache', $bag->get('Cache-Control')); 150 $this->assertTrue($bag->hasCacheControlDirective('no-cache')); 151 152 $bag->remove('Cache-Control'); 153 $bag->replace(array()); 154 $this->assertEquals('no-cache', $bag->get('Cache-Control')); 155 $this->assertTrue($bag->hasCacheControlDirective('no-cache')); 156 } 157 158 public function testCookiesWithSameNames() 159 { 160 $bag = new ResponseHeaderBag(); 161 $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar')); 162 $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar')); 163 $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo')); 164 $bag->setCookie(new Cookie('foo', 'bar')); 165 166 $this->assertCount(4, $bag->getCookies()); 167 168 $headers = explode("\r\n", $bag->__toString()); 169 $this->assertContains('Set-Cookie: foo=bar; path=/path/foo; domain=foo.bar; httponly', $headers); 170 $this->assertContains('Set-Cookie: foo=bar; path=/path/foo; domain=foo.bar; httponly', $headers); 171 $this->assertContains('Set-Cookie: foo=bar; path=/path/bar; domain=bar.foo; httponly', $headers); 172 $this->assertContains('Set-Cookie: foo=bar; path=/; httponly', $headers); 173 174 $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); 175 $this->assertTrue(isset($cookies['foo.bar']['/path/foo']['foo'])); 176 $this->assertTrue(isset($cookies['foo.bar']['/path/bar']['foo'])); 177 $this->assertTrue(isset($cookies['bar.foo']['/path/bar']['foo'])); 178 $this->assertTrue(isset($cookies['']['/']['foo'])); 179 } 180 181 public function testRemoveCookie() 182 { 183 $bag = new ResponseHeaderBag(); 184 $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar')); 185 $bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar')); 186 187 $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); 188 $this->assertTrue(isset($cookies['foo.bar']['/path/foo'])); 189 190 $bag->removeCookie('foo', '/path/foo', 'foo.bar'); 191 192 $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); 193 $this->assertFalse(isset($cookies['foo.bar']['/path/foo'])); 194 195 $bag->removeCookie('bar', '/path/bar', 'foo.bar'); 196 197 $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); 198 $this->assertFalse(isset($cookies['foo.bar'])); 199 } 200 201 public function testRemoveCookieWithNullRemove() 202 { 203 $bag = new ResponseHeaderBag(); 204 $bag->setCookie(new Cookie('foo', 'bar', 0)); 205 $bag->setCookie(new Cookie('bar', 'foo', 0)); 206 207 $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); 208 $this->assertTrue(isset($cookies['']['/'])); 209 210 $bag->removeCookie('foo', null); 211 $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); 212 $this->assertFalse(isset($cookies['']['/']['foo'])); 213 214 $bag->removeCookie('bar', null); 215 $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); 216 $this->assertFalse(isset($cookies['']['/']['bar'])); 217 } 218 219 /** 220 * @expectedException \InvalidArgumentException 221 */ 222 public function testGetCookiesWithInvalidArgument() 223 { 224 $bag = new ResponseHeaderBag(); 225 226 $cookies = $bag->getCookies('invalid_argument'); 227 } 228 229 /** 230 * @expectedException \InvalidArgumentException 231 */ 232 public function testMakeDispositionInvalidDisposition() 233 { 234 $headers = new ResponseHeaderBag(); 235 236 $headers->makeDisposition('invalid', 'foo.html'); 237 } 238 239 /** 240 * @dataProvider provideMakeDisposition 241 */ 242 public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected) 243 { 244 $headers = new ResponseHeaderBag(); 245 246 $this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback)); 247 } 248 249 public function testToStringDoesntMessUpHeaders() 250 { 251 $headers = new ResponseHeaderBag(); 252 253 $headers->set('Location', 'http://www.symfony.com'); 254 $headers->set('Content-type', 'text/html'); 255 256 (string) $headers; 257 258 $allHeaders = $headers->allPreserveCase(); 259 $this->assertEquals(array('http://www.symfony.com'), $allHeaders['Location']); 260 $this->assertEquals(array('text/html'), $allHeaders['Content-type']); 261 } 262 263 public function provideMakeDisposition() 264 { 265 return array( 266 array('attachment', 'foo.html', 'foo.html', 'attachment; filename="foo.html"'), 267 array('attachment', 'foo.html', '', 'attachment; filename="foo.html"'), 268 array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'), 269 array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'), 270 array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'), 271 array('attachment', 'föö.html', 'foo.html', 'attachment; filename="foo.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html'), 272 ); 273 } 274 275 /** 276 * @dataProvider provideMakeDispositionFail 277 * @expectedException \InvalidArgumentException 278 */ 279 public function testMakeDispositionFail($disposition, $filename) 280 { 281 $headers = new ResponseHeaderBag(); 282 283 $headers->makeDisposition($disposition, $filename); 284 } 285 286 public function provideMakeDispositionFail() 287 { 288 return array( 289 array('attachment', 'foo%20bar.html'), 290 array('attachment', 'foo/bar.html'), 291 array('attachment', '/foo.html'), 292 array('attachment', 'foo\bar.html'), 293 array('attachment', '\foo.html'), 294 array('attachment', 'föö.html'), 295 ); 296 } 297} 298