1<?php 2/** 3 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org) 4 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 5 * 6 * Licensed under The MIT License 7 * For full copyright and license information, please see the LICENSE.txt 8 * Redistributions of files must retain the above copyright notice. 9 * 10 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 11 * @link https://cakephp.org CakePHP(tm) Project 12 * @package Cake.Test.Case.Network 13 * @since CakePHP(tm) v 2.0 14 * @license https://opensource.org/licenses/mit-license.php MIT License 15 */ 16 17App::uses('CakeResponse', 'Network'); 18App::uses('CakeRequest', 'Network'); 19 20/** 21 * CakeResponseTest 22 * 23 * @package Cake.Test.Case.Network 24 */ 25class CakeResponseTest extends CakeTestCase { 26 27/** 28 * Setup for tests 29 * 30 * @return void 31 */ 32 public function setUp() { 33 parent::setUp(); 34 ob_start(); 35 } 36 37/** 38 * Cleanup after tests 39 * 40 * @return void 41 */ 42 public function tearDown() { 43 parent::tearDown(); 44 ob_end_clean(); 45 } 46 47/** 48 * Tests the request object constructor 49 * 50 * @return void 51 */ 52 public function testConstruct() { 53 $response = new CakeResponse(); 54 $this->assertNull($response->body()); 55 $this->assertEquals('UTF-8', $response->charset()); 56 $this->assertEquals('text/html', $response->type()); 57 $this->assertEquals(200, $response->statusCode()); 58 59 $options = array( 60 'body' => 'This is the body', 61 'charset' => 'my-custom-charset', 62 'type' => 'mp3', 63 'status' => '203' 64 ); 65 $response = new CakeResponse($options); 66 $this->assertEquals('This is the body', $response->body()); 67 $this->assertEquals('my-custom-charset', $response->charset()); 68 $this->assertEquals('audio/mpeg', $response->type()); 69 $this->assertEquals(203, $response->statusCode()); 70 71 $options = array( 72 'body' => 'This is the body', 73 'charset' => 'my-custom-charset', 74 'type' => 'mp3', 75 'status' => '422', 76 'statusCodes' => array( 77 422 => 'Unprocessable Entity' 78 ) 79 ); 80 $response = new CakeResponse($options); 81 $this->assertEquals($options['body'], $response->body()); 82 $this->assertEquals($options['charset'], $response->charset()); 83 $this->assertEquals($response->getMimeType($options['type']), $response->type()); 84 $this->assertEquals($options['status'], $response->statusCode()); 85 } 86 87/** 88 * Tests the body method 89 * 90 * @return void 91 */ 92 public function testBody() { 93 $response = new CakeResponse(); 94 $this->assertNull($response->body()); 95 $response->body('Response body'); 96 $this->assertEquals('Response body', $response->body()); 97 $this->assertEquals('Changed Body', $response->body('Changed Body')); 98 } 99 100/** 101 * Tests the charset method 102 * 103 * @return void 104 */ 105 public function testCharset() { 106 $response = new CakeResponse(); 107 $this->assertEquals('UTF-8', $response->charset()); 108 $response->charset('iso-8859-1'); 109 $this->assertEquals('iso-8859-1', $response->charset()); 110 $this->assertEquals('UTF-16', $response->charset('UTF-16')); 111 } 112 113/** 114 * Tests the statusCode method 115 * 116 * @expectedException CakeException 117 * @return void 118 */ 119 public function testStatusCode() { 120 $response = new CakeResponse(); 121 $this->assertEquals(200, $response->statusCode()); 122 $response->statusCode(404); 123 $this->assertEquals(404, $response->statusCode()); 124 $this->assertEquals(500, $response->statusCode(500)); 125 126 //Throws exception 127 $response->statusCode(1001); 128 } 129 130/** 131 * Tests the type method 132 * 133 * @return void 134 */ 135 public function testType() { 136 $response = new CakeResponse(); 137 $this->assertEquals('text/html', $response->type()); 138 $response->type('pdf'); 139 $this->assertEquals('application/pdf', $response->type()); 140 $this->assertEquals('application/crazy-mime', $response->type('application/crazy-mime')); 141 $this->assertEquals('application/json', $response->type('json')); 142 $this->assertEquals('text/vnd.wap.wml', $response->type('wap')); 143 $this->assertEquals('application/vnd.wap.xhtml+xml', $response->type('xhtml-mobile')); 144 $this->assertEquals('text/csv', $response->type('csv')); 145 146 $response->type(array('keynote' => 'application/keynote', 'bat' => 'application/bat')); 147 $this->assertEquals('application/keynote', $response->type('keynote')); 148 $this->assertEquals('application/bat', $response->type('bat')); 149 150 $this->assertFalse($response->type('wackytype')); 151 } 152 153/** 154 * Tests the header method 155 * 156 * @return void 157 */ 158 public function testHeader() { 159 $response = new CakeResponse(); 160 $headers = array(); 161 $this->assertEquals($headers, $response->header()); 162 163 $response->header('Location', 'http://example.com'); 164 $headers += array('Location' => 'http://example.com'); 165 $this->assertEquals($headers, $response->header()); 166 167 // Headers with the same name are overwritten 168 $response->header('Location', 'http://example2.com'); 169 $headers = array('Location' => 'http://example2.com'); 170 $this->assertEquals($headers, $response->header()); 171 172 $response->header('Date', null); 173 $headers += array('Date' => null); 174 $this->assertEquals($headers, $response->header()); 175 176 $response->header(array('WWW-Authenticate' => 'Negotiate')); 177 $headers += array('WWW-Authenticate' => 'Negotiate'); 178 $this->assertEquals($headers, $response->header()); 179 180 $response->header(array('WWW-Authenticate' => 'Not-Negotiate')); 181 $headers['WWW-Authenticate'] = 'Not-Negotiate'; 182 $this->assertEquals($headers, $response->header()); 183 184 $response->header(array('Age' => 12, 'Allow' => 'GET, HEAD')); 185 $headers += array('Age' => 12, 'Allow' => 'GET, HEAD'); 186 $this->assertEquals($headers, $response->header()); 187 188 // String headers are allowed 189 $response->header('Content-Language: da'); 190 $headers += array('Content-Language' => 'da'); 191 $this->assertEquals($headers, $response->header()); 192 193 $response->header('Content-Language: da'); 194 $headers += array('Content-Language' => 'da'); 195 $this->assertEquals($headers, $response->header()); 196 197 $response->header(array('Content-Encoding: gzip', 'Vary: *', 'Pragma' => 'no-cache')); 198 $headers += array('Content-Encoding' => 'gzip', 'Vary' => '*', 'Pragma' => 'no-cache'); 199 $this->assertEquals($headers, $response->header()); 200 201 $response->header('Access-Control-Allow-Origin', array('domain1', 'domain2')); 202 $headers += array('Access-Control-Allow-Origin' => array('domain1', 'domain2')); 203 $this->assertEquals($headers, $response->header()); 204 } 205 206/** 207 * Tests the send method 208 * 209 * @return void 210 */ 211 public function testSend() { 212 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent', '_setCookies')); 213 $response->header(array( 214 'Content-Language' => 'es', 215 'WWW-Authenticate' => 'Negotiate', 216 'Access-Control-Allow-Origin' => array('domain1', 'domain2'), 217 )); 218 $response->body('the response body'); 219 $response->expects($this->once())->method('_sendContent')->with('the response body'); 220 $response->expects($this->at(0))->method('_setCookies'); 221 $response->expects($this->at(1)) 222 ->method('_sendHeader')->with('HTTP/1.1 200 OK'); 223 $response->expects($this->at(2)) 224 ->method('_sendHeader')->with('Content-Language', 'es'); 225 $response->expects($this->at(3)) 226 ->method('_sendHeader')->with('WWW-Authenticate', 'Negotiate'); 227 $response->expects($this->at(4)) 228 ->method('_sendHeader')->with('Access-Control-Allow-Origin', 'domain1'); 229 $response->expects($this->at(5)) 230 ->method('_sendHeader')->with('Access-Control-Allow-Origin', 'domain2'); 231 $response->expects($this->at(6)) 232 ->method('_sendHeader')->with('Content-Length', 17); 233 $response->expects($this->at(7)) 234 ->method('_sendHeader')->with('Content-Type', 'text/html; charset=UTF-8'); 235 $response->send(); 236 } 237 238/** 239 * Data provider for content type tests. 240 * 241 * @return array 242 */ 243 public static function charsetTypeProvider() { 244 return array( 245 array('mp3', 'audio/mpeg'), 246 array('js', 'application/javascript; charset=UTF-8'), 247 array('json', 'application/json; charset=UTF-8'), 248 array('xml', 'application/xml; charset=UTF-8'), 249 array('txt', 'text/plain; charset=UTF-8'), 250 ); 251 } 252 253/** 254 * Tests the send method and changing the content type 255 * 256 * @dataProvider charsetTypeProvider 257 * @return void 258 */ 259 public function testSendChangingContentType($original, $expected) { 260 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent', '_setCookies')); 261 $response->type($original); 262 $response->body('the response body'); 263 $response->expects($this->once())->method('_sendContent')->with('the response body'); 264 $response->expects($this->at(0))->method('_setCookies'); 265 $response->expects($this->at(1)) 266 ->method('_sendHeader')->with('HTTP/1.1 200 OK'); 267 $response->expects($this->at(2)) 268 ->method('_sendHeader')->with('Content-Length', 17); 269 $response->expects($this->at(3)) 270 ->method('_sendHeader')->with('Content-Type', $expected); 271 $response->send(); 272 } 273 274/** 275 * Tests the send method and changing the content type to JS without adding the charset 276 * 277 * @return void 278 */ 279 public function testSendChangingContentTypeWithoutCharset() { 280 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent', '_setCookies')); 281 $response->type('js'); 282 $response->charset(''); 283 284 $response->body('var $foo = "bar";'); 285 $response->expects($this->once())->method('_sendContent')->with('var $foo = "bar";'); 286 $response->expects($this->at(0))->method('_setCookies'); 287 $response->expects($this->at(1)) 288 ->method('_sendHeader')->with('HTTP/1.1 200 OK'); 289 $response->expects($this->at(2)) 290 ->method('_sendHeader')->with('Content-Length', 17); 291 $response->expects($this->at(3)) 292 ->method('_sendHeader')->with('Content-Type', 'application/javascript'); 293 $response->send(); 294 } 295 296/** 297 * Tests the send method and changing the content type 298 * 299 * @return void 300 */ 301 public function testSendWithLocation() { 302 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent', '_setCookies')); 303 $response->header('Location', 'http://www.example.com'); 304 $response->expects($this->at(0))->method('_setCookies'); 305 $response->expects($this->at(1)) 306 ->method('_sendHeader')->with('HTTP/1.1 302 Found'); 307 $response->expects($this->at(2)) 308 ->method('_sendHeader')->with('Location', 'http://www.example.com'); 309 $response->expects($this->at(3)) 310 ->method('_sendHeader')->with('Content-Type', 'text/html; charset=UTF-8'); 311 $response->send(); 312 } 313 314/** 315 * Tests the disableCache method 316 * 317 * @return void 318 */ 319 public function testDisableCache() { 320 $response = new CakeResponse(); 321 $expected = array( 322 'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT', 323 'Last-Modified' => gmdate("D, d M Y H:i:s") . " GMT", 324 'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0' 325 ); 326 $response->disableCache(); 327 $this->assertEquals($expected, $response->header()); 328 } 329 330/** 331 * Tests the cache method 332 * 333 * @return void 334 */ 335 public function testCache() { 336 $response = new CakeResponse(); 337 $since = time(); 338 $time = new DateTime('+1 day', new DateTimeZone('UTC')); 339 $response->expires('+1 day'); 340 $expected = array( 341 'Date' => gmdate("D, j M Y G:i:s ", $since) . 'GMT', 342 'Last-Modified' => gmdate("D, j M Y H:i:s ", $since) . 'GMT', 343 'Expires' => $time->format('D, j M Y H:i:s') . ' GMT', 344 'Cache-Control' => 'public, max-age=' . ($time->format('U') - time()) 345 ); 346 $response->cache($since); 347 $this->assertEquals($expected, $response->header()); 348 349 $response = new CakeResponse(); 350 $since = time(); 351 $time = '+5 day'; 352 $expected = array( 353 'Date' => gmdate("D, j M Y G:i:s ", $since) . 'GMT', 354 'Last-Modified' => gmdate("D, j M Y H:i:s ", $since) . 'GMT', 355 'Expires' => gmdate("D, j M Y H:i:s", strtotime($time)) . " GMT", 356 'Cache-Control' => 'public, max-age=' . (strtotime($time) - time()) 357 ); 358 $response->cache($since, $time); 359 $this->assertEquals($expected, $response->header()); 360 361 $response = new CakeResponse(); 362 $since = time(); 363 $time = time(); 364 $expected = array( 365 'Date' => gmdate("D, j M Y G:i:s ", $since) . 'GMT', 366 'Last-Modified' => gmdate("D, j M Y H:i:s ", $since) . 'GMT', 367 'Expires' => gmdate("D, j M Y H:i:s", $time) . " GMT", 368 'Cache-Control' => 'public, max-age=0' 369 ); 370 $response->cache($since, $time); 371 $this->assertEquals($expected, $response->header()); 372 } 373 374/** 375 * Tests the compress method 376 * 377 * @return void 378 */ 379 public function testCompress() { 380 if (PHP_SAPI !== 'cli') { 381 $this->markTestSkipped('The response compression can only be tested in cli.'); 382 } 383 384 $response = new CakeResponse(); 385 if (ini_get("zlib.output_compression") === '1' || !extension_loaded("zlib")) { 386 $this->assertFalse($response->compress()); 387 $this->markTestSkipped('Is not possible to test output compression'); 388 } 389 390 $_SERVER['HTTP_ACCEPT_ENCODING'] = ''; 391 $result = $response->compress(); 392 $this->assertFalse($result); 393 394 $_SERVER['HTTP_ACCEPT_ENCODING'] = 'gzip'; 395 $result = $response->compress(); 396 $this->assertTrue($result); 397 $this->assertTrue(in_array('ob_gzhandler', ob_list_handlers())); 398 399 ob_get_clean(); 400 } 401 402/** 403 * Tests the httpCodes method 404 * 405 * @expectedException CakeException 406 * @return void 407 */ 408 public function testHttpCodes() { 409 $response = new CakeResponse(); 410 $result = $response->httpCodes(); 411 $this->assertEquals(41, count($result)); 412 413 $result = $response->httpCodes(100); 414 $expected = array(100 => 'Continue'); 415 $this->assertEquals($expected, $result); 416 417 $codes = array( 418 381 => 'Unicorn Moved', 419 555 => 'Unexpected Minotaur' 420 ); 421 422 $result = $response->httpCodes($codes); 423 $this->assertTrue($result); 424 $this->assertEquals(43, count($response->httpCodes())); 425 426 $result = $response->httpCodes(381); 427 $expected = array(381 => 'Unicorn Moved'); 428 $this->assertEquals($expected, $result); 429 430 $codes = array(404 => 'Sorry Bro'); 431 $result = $response->httpCodes($codes); 432 $this->assertTrue($result); 433 $this->assertEquals(43, count($response->httpCodes())); 434 435 $result = $response->httpCodes(404); 436 $expected = array(404 => 'Sorry Bro'); 437 $this->assertEquals($expected, $result); 438 439 //Throws exception 440 $response->httpCodes(array( 441 0 => 'Nothing Here', 442 -1 => 'Reverse Infinity', 443 12345 => 'Universal Password', 444 'Hello' => 'World' 445 )); 446 } 447 448/** 449 * Tests the download method 450 * 451 * @return void 452 */ 453 public function testDownload() { 454 $response = new CakeResponse(); 455 $expected = array( 456 'Content-Disposition' => 'attachment; filename="myfile.mp3"' 457 ); 458 $response->download('myfile.mp3'); 459 $this->assertEquals($expected, $response->header()); 460 } 461 462/** 463 * Tests the mapType method 464 * 465 * @return void 466 */ 467 public function testMapType() { 468 $response = new CakeResponse(); 469 $this->assertEquals('wav', $response->mapType('audio/x-wav')); 470 $this->assertEquals('pdf', $response->mapType('application/pdf')); 471 $this->assertEquals('xml', $response->mapType('text/xml')); 472 $this->assertEquals('html', $response->mapType('*/*')); 473 $this->assertEquals('csv', $response->mapType('application/vnd.ms-excel')); 474 $expected = array('json', 'xhtml', 'css'); 475 $result = $response->mapType(array('application/json', 'application/xhtml+xml', 'text/css')); 476 $this->assertEquals($expected, $result); 477 } 478 479/** 480 * Tests the outputCompressed method 481 * 482 * @return void 483 */ 484 public function testOutputCompressed() { 485 $response = new CakeResponse(); 486 487 $_SERVER['HTTP_ACCEPT_ENCODING'] = 'gzip'; 488 $result = $response->outputCompressed(); 489 $this->assertFalse($result); 490 491 $_SERVER['HTTP_ACCEPT_ENCODING'] = ''; 492 $result = $response->outputCompressed(); 493 $this->assertFalse($result); 494 495 if (!extension_loaded("zlib")) { 496 $this->markTestSkipped('Skipping further tests for outputCompressed as zlib extension is not loaded'); 497 } 498 if (PHP_SAPI !== 'cli') { 499 $this->markTestSkipped('Testing outputCompressed method with compression enabled done only in cli'); 500 } 501 502 if (ini_get("zlib.output_compression") !== '1') { 503 ob_start('ob_gzhandler'); 504 } 505 $_SERVER['HTTP_ACCEPT_ENCODING'] = 'gzip'; 506 $result = $response->outputCompressed(); 507 $this->assertTrue($result); 508 509 $_SERVER['HTTP_ACCEPT_ENCODING'] = ''; 510 $result = $response->outputCompressed(); 511 $this->assertFalse($result); 512 if (ini_get("zlib.output_compression") !== '1') { 513 ob_get_clean(); 514 } 515 } 516 517/** 518 * Tests the send and setting of Content-Length 519 * 520 * @return void 521 */ 522 public function testSendContentLength() { 523 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 524 $response->body('the response body'); 525 $response->expects($this->once())->method('_sendContent')->with('the response body'); 526 $response->expects($this->at(0)) 527 ->method('_sendHeader')->with('HTTP/1.1 200 OK'); 528 $response->expects($this->at(2)) 529 ->method('_sendHeader')->with('Content-Type', 'text/html; charset=UTF-8'); 530 $response->expects($this->at(1)) 531 ->method('_sendHeader')->with('Content-Length', strlen('the response body')); 532 $response->send(); 533 534 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 535 $body = '長い長い長いSubjectの場合はfoldingするのが正しいんだけどいったいどうなるんだろう?'; 536 $response->body($body); 537 $response->expects($this->once())->method('_sendContent')->with($body); 538 $response->expects($this->at(0)) 539 ->method('_sendHeader')->with('HTTP/1.1 200 OK'); 540 $response->expects($this->at(2)) 541 ->method('_sendHeader')->with('Content-Type', 'text/html; charset=UTF-8'); 542 $response->expects($this->at(1)) 543 ->method('_sendHeader')->with('Content-Length', 116); 544 $response->send(); 545 546 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent', 'outputCompressed')); 547 $body = '長い長い長いSubjectの場合はfoldingするのが正しいんだけどいったいどうなるんだろう?'; 548 $response->body($body); 549 $response->expects($this->once())->method('outputCompressed')->will($this->returnValue(true)); 550 $response->expects($this->once())->method('_sendContent')->with($body); 551 $response->expects($this->exactly(2))->method('_sendHeader'); 552 $response->send(); 553 554 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent', 'outputCompressed')); 555 $body = 'hwy'; 556 $response->body($body); 557 $response->header('Content-Length', 1); 558 $response->expects($this->never())->method('outputCompressed'); 559 $response->expects($this->once())->method('_sendContent')->with($body); 560 $response->expects($this->at(1)) 561 ->method('_sendHeader')->with('Content-Length', 1); 562 $response->send(); 563 564 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 565 $body = 'content'; 566 $response->statusCode(301); 567 $response->body($body); 568 $response->expects($this->once())->method('_sendContent')->with($body); 569 $response->expects($this->exactly(2))->method('_sendHeader'); 570 $response->send(); 571 572 ob_start(); 573 $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 574 $goofyOutput = 'I am goofily sending output in the controller'; 575 echo $goofyOutput; 576 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 577 $body = '長い長い長いSubjectの場合はfoldingするのが正しいんだけどいったいどうなるんだろう?'; 578 $response->body($body); 579 $response->expects($this->once())->method('_sendContent')->with($body); 580 $response->expects($this->at(0)) 581 ->method('_sendHeader')->with('HTTP/1.1 200 OK'); 582 $response->expects($this->at(1)) 583 ->method('_sendHeader')->with('Content-Length', strlen($goofyOutput) + 116); 584 $response->expects($this->at(2)) 585 ->method('_sendHeader')->with('Content-Type', 'text/html; charset=UTF-8'); 586 $response->send(); 587 ob_end_clean(); 588 } 589 590/** 591 * Tests getting/setting the protocol 592 * 593 * @return void 594 */ 595 public function testProtocol() { 596 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 597 $response->protocol('HTTP/1.0'); 598 $this->assertEquals('HTTP/1.0', $response->protocol()); 599 $response->expects($this->at(0)) 600 ->method('_sendHeader')->with('HTTP/1.0 200 OK'); 601 $response->send(); 602 } 603 604/** 605 * Tests getting/setting the Content-Length 606 * 607 * @return void 608 */ 609 public function testLength() { 610 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 611 $response->length(100); 612 $this->assertEquals(100, $response->length()); 613 $response->expects($this->at(1)) 614 ->method('_sendHeader')->with('Content-Length', 100); 615 $response->send(); 616 617 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 618 $response->length(false); 619 $this->assertFalse($response->length()); 620 $response->expects($this->exactly(2)) 621 ->method('_sendHeader'); 622 $response->send(); 623 } 624 625/** 626 * Tests that the response body is unset if the status code is 304 or 204 627 * 628 * @return void 629 */ 630 public function testUnmodifiedContent() { 631 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 632 $response->body('This is a body'); 633 $response->statusCode(204); 634 $response->expects($this->once()) 635 ->method('_sendContent')->with(''); 636 $response->send(); 637 $this->assertFalse(array_key_exists('Content-Type', $response->header())); 638 639 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 640 $response->body('This is a body'); 641 $response->statusCode(304); 642 $response->expects($this->once()) 643 ->method('_sendContent')->with(''); 644 $response->send(); 645 646 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 647 $response->body('This is a body'); 648 $response->statusCode(200); 649 $response->expects($this->once()) 650 ->method('_sendContent')->with('This is a body'); 651 $response->send(); 652 } 653 654/** 655 * Tests setting the expiration date 656 * 657 * @return void 658 */ 659 public function testExpires() { 660 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 661 $now = new DateTime('now', new DateTimeZone('America/Los_Angeles')); 662 $response->expires($now); 663 $now->setTimeZone(new DateTimeZone('UTC')); 664 $this->assertEquals($now->format('D, j M Y H:i:s') . ' GMT', $response->expires()); 665 $response->expects($this->at(1)) 666 ->method('_sendHeader')->with('Expires', $now->format('D, j M Y H:i:s') . ' GMT'); 667 $response->send(); 668 669 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 670 $now = time(); 671 $response->expires($now); 672 $this->assertEquals(gmdate('D, j M Y H:i:s', $now) . ' GMT', $response->expires()); 673 $response->expects($this->at(1)) 674 ->method('_sendHeader')->with('Expires', gmdate('D, j M Y H:i:s', $now) . ' GMT'); 675 $response->send(); 676 677 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 678 $time = new DateTime('+1 day', new DateTimeZone('UTC')); 679 $response->expires('+1 day'); 680 $this->assertEquals($time->format('D, j M Y H:i:s') . ' GMT', $response->expires()); 681 $response->expects($this->at(1)) 682 ->method('_sendHeader')->with('Expires', $time->format('D, j M Y H:i:s') . ' GMT'); 683 $response->send(); 684 } 685 686/** 687 * Tests setting the modification date 688 * 689 * @return void 690 */ 691 public function testModified() { 692 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 693 $now = new DateTime('now', new DateTimeZone('America/Los_Angeles')); 694 $response->modified($now); 695 $now->setTimeZone(new DateTimeZone('UTC')); 696 $this->assertEquals($now->format('D, j M Y H:i:s') . ' GMT', $response->modified()); 697 $response->expects($this->at(1)) 698 ->method('_sendHeader')->with('Last-Modified', $now->format('D, j M Y H:i:s') . ' GMT'); 699 $response->send(); 700 701 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 702 $now = time(); 703 $response->modified($now); 704 $this->assertEquals(gmdate('D, j M Y H:i:s', $now) . ' GMT', $response->modified()); 705 $response->expects($this->at(1)) 706 ->method('_sendHeader')->with('Last-Modified', gmdate('D, j M Y H:i:s', $now) . ' GMT'); 707 $response->send(); 708 709 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 710 $time = new DateTime('+1 day', new DateTimeZone('UTC')); 711 $response->modified('+1 day'); 712 $this->assertEquals($time->format('D, j M Y H:i:s') . ' GMT', $response->modified()); 713 $response->expects($this->at(1)) 714 ->method('_sendHeader')->with('Last-Modified', $time->format('D, j M Y H:i:s') . ' GMT'); 715 $response->send(); 716 } 717 718/** 719 * Tests setting of public/private Cache-Control directives 720 * 721 * @return void 722 */ 723 public function testSharable() { 724 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 725 $this->assertNull($response->sharable()); 726 $response->sharable(true); 727 $headers = $response->header(); 728 $this->assertEquals('public', $headers['Cache-Control']); 729 $response->expects($this->at(1)) 730 ->method('_sendHeader')->with('Cache-Control', 'public'); 731 $response->send(); 732 733 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 734 $response->sharable(false); 735 $headers = $response->header(); 736 $this->assertEquals('private', $headers['Cache-Control']); 737 $response->expects($this->at(1)) 738 ->method('_sendHeader')->with('Cache-Control', 'private'); 739 $response->send(); 740 741 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 742 $response->sharable(true); 743 $headers = $response->header(); 744 $this->assertEquals('public', $headers['Cache-Control']); 745 $response->sharable(false); 746 $headers = $response->header(); 747 $this->assertEquals('private', $headers['Cache-Control']); 748 $response->expects($this->at(1)) 749 ->method('_sendHeader')->with('Cache-Control', 'private'); 750 $response->send(); 751 $this->assertFalse($response->sharable()); 752 $response->sharable(true); 753 $this->assertTrue($response->sharable()); 754 755 $response = new CakeResponse; 756 $response->sharable(true, 3600); 757 $headers = $response->header(); 758 $this->assertEquals('public, max-age=3600', $headers['Cache-Control']); 759 760 $response = new CakeResponse; 761 $response->sharable(false, 3600); 762 $headers = $response->header(); 763 $this->assertEquals('private, max-age=3600', $headers['Cache-Control']); 764 $response->send(); 765 } 766 767/** 768 * Tests setting of max-age Cache-Control directive 769 * 770 * @return void 771 */ 772 public function testMaxAge() { 773 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 774 $this->assertNull($response->maxAge()); 775 $response->maxAge(3600); 776 $this->assertEquals(3600, $response->maxAge()); 777 $headers = $response->header(); 778 $this->assertEquals('max-age=3600', $headers['Cache-Control']); 779 $response->expects($this->at(1)) 780 ->method('_sendHeader')->with('Cache-Control', 'max-age=3600'); 781 $response->send(); 782 783 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 784 $response->maxAge(3600); 785 $response->sharable(false); 786 $headers = $response->header(); 787 $this->assertEquals('max-age=3600, private', $headers['Cache-Control']); 788 $response->expects($this->at(1)) 789 ->method('_sendHeader')->with('Cache-Control', 'max-age=3600, private'); 790 $response->send(); 791 } 792 793/** 794 * Tests setting of s-maxage Cache-Control directive 795 * 796 * @return void 797 */ 798 public function testSharedMaxAge() { 799 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 800 $this->assertNull($response->maxAge()); 801 $response->sharedMaxAge(3600); 802 $this->assertEquals(3600, $response->sharedMaxAge()); 803 $headers = $response->header(); 804 $this->assertEquals('s-maxage=3600', $headers['Cache-Control']); 805 $response->expects($this->at(1)) 806 ->method('_sendHeader')->with('Cache-Control', 's-maxage=3600'); 807 $response->send(); 808 809 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 810 $response->sharedMaxAge(3600); 811 $response->sharable(true); 812 $headers = $response->header(); 813 $this->assertEquals('s-maxage=3600, public', $headers['Cache-Control']); 814 $response->expects($this->at(1)) 815 ->method('_sendHeader')->with('Cache-Control', 's-maxage=3600, public'); 816 $response->send(); 817 } 818 819/** 820 * Tests setting of must-revalidate Cache-Control directive 821 * 822 * @return void 823 */ 824 public function testMustRevalidate() { 825 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 826 $this->assertFalse($response->mustRevalidate()); 827 $response->mustRevalidate(true); 828 $this->assertTrue($response->mustRevalidate()); 829 $headers = $response->header(); 830 $this->assertEquals('must-revalidate', $headers['Cache-Control']); 831 $response->expects($this->at(1)) 832 ->method('_sendHeader')->with('Cache-Control', 'must-revalidate'); 833 $response->send(); 834 $response->mustRevalidate(false); 835 $this->assertFalse($response->mustRevalidate()); 836 837 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 838 $response->sharedMaxAge(3600); 839 $response->mustRevalidate(true); 840 $headers = $response->header(); 841 $this->assertEquals('s-maxage=3600, must-revalidate', $headers['Cache-Control']); 842 $response->expects($this->at(1)) 843 ->method('_sendHeader')->with('Cache-Control', 's-maxage=3600, must-revalidate'); 844 $response->send(); 845 } 846 847/** 848 * Tests getting/setting the Vary header 849 * 850 * @return void 851 */ 852 public function testVary() { 853 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 854 $response->vary('Accept-encoding'); 855 $this->assertEquals(array('Accept-encoding'), $response->vary()); 856 $response->expects($this->at(1)) 857 ->method('_sendHeader')->with('Vary', 'Accept-encoding'); 858 $response->send(); 859 860 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 861 $response->vary(array('Accept-language', 'Accept-encoding')); 862 $response->expects($this->at(1)) 863 ->method('_sendHeader')->with('Vary', 'Accept-language, Accept-encoding'); 864 $response->send(); 865 $this->assertEquals(array('Accept-language', 'Accept-encoding'), $response->vary()); 866 } 867 868/** 869 * Tests getting/setting the Etag header 870 * 871 * @return void 872 */ 873 public function testEtag() { 874 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 875 $response->etag('something'); 876 $this->assertEquals('"something"', $response->etag()); 877 $response->expects($this->at(1)) 878 ->method('_sendHeader')->with('Etag', '"something"'); 879 $response->send(); 880 881 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 882 $response->etag('something', true); 883 $this->assertEquals('W/"something"', $response->etag()); 884 $response->expects($this->at(1)) 885 ->method('_sendHeader')->with('Etag', 'W/"something"'); 886 $response->send(); 887 } 888 889/** 890 * Tests that the response is able to be marked as not modified 891 * 892 * @return void 893 */ 894 public function testNotModified() { 895 $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent')); 896 $response->body('something'); 897 $response->statusCode(200); 898 $response->length(100); 899 $response->modified('now'); 900 $response->notModified(); 901 902 $this->assertEmpty($response->header()); 903 $this->assertEmpty($response->body()); 904 $this->assertEquals(304, $response->statusCode()); 905 } 906 907/** 908 * Test checkNotModified method 909 * 910 * @return void 911 */ 912 public function testCheckNotModifiedByEtagStar() { 913 $_SERVER['HTTP_IF_NONE_MATCH'] = '*'; 914 $response = $this->getMock('CakeResponse', array('notModified')); 915 $response->etag('something'); 916 $response->expects($this->once())->method('notModified'); 917 $response->checkNotModified(new CakeRequest); 918 } 919 920/** 921 * Test checkNotModified method 922 * 923 * @return void 924 */ 925 public function testCheckNotModifiedByEtagExact() { 926 $_SERVER['HTTP_IF_NONE_MATCH'] = 'W/"something", "other"'; 927 $response = $this->getMock('CakeResponse', array('notModified')); 928 $response->etag('something', true); 929 $response->expects($this->once())->method('notModified'); 930 $this->assertTrue($response->checkNotModified(new CakeRequest)); 931 } 932 933/** 934 * Test checkNotModified method 935 * 936 * @return void 937 */ 938 public function testCheckNotModifiedByEtagAndTime() { 939 $_SERVER['HTTP_IF_NONE_MATCH'] = 'W/"something", "other"'; 940 $_SERVER['HTTP_IF_MODIFIED_SINCE'] = '2012-01-01 00:00:00'; 941 $response = $this->getMock('CakeResponse', array('notModified')); 942 $response->etag('something', true); 943 $response->modified('2012-01-01 00:00:00'); 944 $response->expects($this->once())->method('notModified'); 945 $this->assertTrue($response->checkNotModified(new CakeRequest)); 946 } 947 948/** 949 * Test checkNotModified method 950 * 951 * @return void 952 */ 953 public function testCheckNotModifiedByEtagAndTimeMismatch() { 954 $_SERVER['HTTP_IF_NONE_MATCH'] = 'W/"something", "other"'; 955 $_SERVER['HTTP_IF_MODIFIED_SINCE'] = '2012-01-01 00:00:00'; 956 $response = $this->getMock('CakeResponse', array('notModified')); 957 $response->etag('something', true); 958 $response->modified('2012-01-01 00:00:01'); 959 $response->expects($this->never())->method('notModified'); 960 $this->assertFalse($response->checkNotModified(new CakeRequest)); 961 } 962 963/** 964 * Test checkNotModified method 965 * 966 * @return void 967 */ 968 public function testCheckNotModifiedByEtagMismatch() { 969 $_SERVER['HTTP_IF_NONE_MATCH'] = 'W/"something-else", "other"'; 970 $_SERVER['HTTP_IF_MODIFIED_SINCE'] = '2012-01-01 00:00:00'; 971 $response = $this->getMock('CakeResponse', array('notModified')); 972 $response->etag('something', true); 973 $response->modified('2012-01-01 00:00:00'); 974 $response->expects($this->never())->method('notModified'); 975 $this->assertFalse($response->checkNotModified(new CakeRequest)); 976 } 977 978/** 979 * Test checkNotModified method 980 * 981 * @return void 982 */ 983 public function testCheckNotModifiedByTime() { 984 $_SERVER['HTTP_IF_MODIFIED_SINCE'] = '2012-01-01 00:00:00'; 985 $response = $this->getMock('CakeResponse', array('notModified')); 986 $response->modified('2012-01-01 00:00:00'); 987 $response->expects($this->once())->method('notModified'); 988 $this->assertTrue($response->checkNotModified(new CakeRequest)); 989 } 990 991/** 992 * Test checkNotModified method 993 * 994 * @return void 995 */ 996 public function testCheckNotModifiedNoHints() { 997 $_SERVER['HTTP_IF_NONE_MATCH'] = 'W/"something", "other"'; 998 $_SERVER['HTTP_IF_MODIFIED_SINCE'] = '2012-01-01 00:00:00'; 999 $response = $this->getMock('CakeResponse', array('notModified')); 1000 $response->expects($this->never())->method('notModified'); 1001 $this->assertFalse($response->checkNotModified(new CakeRequest)); 1002 } 1003 1004/** 1005 * Test cookie setting 1006 * 1007 * @return void 1008 */ 1009 public function testCookieSettings() { 1010 $response = new CakeResponse(); 1011 $cookie = array( 1012 'name' => 'CakeTestCookie[Testing]' 1013 ); 1014 $response->cookie($cookie); 1015 $expected = array( 1016 'name' => 'CakeTestCookie[Testing]', 1017 'value' => '', 1018 'expire' => 0, 1019 'path' => '/', 1020 'domain' => '', 1021 'secure' => false, 1022 'httpOnly' => false); 1023 $result = $response->cookie('CakeTestCookie[Testing]'); 1024 $this->assertEquals($expected, $result); 1025 1026 $cookie = array( 1027 'name' => 'CakeTestCookie[Testing2]', 1028 'value' => '[a,b,c]', 1029 'expire' => 1000, 1030 'path' => '/test', 1031 'secure' => true 1032 ); 1033 $response->cookie($cookie); 1034 $expected = array( 1035 'CakeTestCookie[Testing]' => array( 1036 'name' => 'CakeTestCookie[Testing]', 1037 'value' => '', 1038 'expire' => 0, 1039 'path' => '/', 1040 'domain' => '', 1041 'secure' => false, 1042 'httpOnly' => false 1043 ), 1044 'CakeTestCookie[Testing2]' => array( 1045 'name' => 'CakeTestCookie[Testing2]', 1046 'value' => '[a,b,c]', 1047 'expire' => 1000, 1048 'path' => '/test', 1049 'domain' => '', 1050 'secure' => true, 1051 'httpOnly' => false 1052 ) 1053 ); 1054 1055 $result = $response->cookie(); 1056 $this->assertEquals($expected, $result); 1057 1058 $cookie = $expected['CakeTestCookie[Testing]']; 1059 $cookie['value'] = 'test'; 1060 $response->cookie($cookie); 1061 $expected = array( 1062 'CakeTestCookie[Testing]' => array( 1063 'name' => 'CakeTestCookie[Testing]', 1064 'value' => 'test', 1065 'expire' => 0, 1066 'path' => '/', 1067 'domain' => '', 1068 'secure' => false, 1069 'httpOnly' => false 1070 ), 1071 'CakeTestCookie[Testing2]' => array( 1072 'name' => 'CakeTestCookie[Testing2]', 1073 'value' => '[a,b,c]', 1074 'expire' => 1000, 1075 'path' => '/test', 1076 'domain' => '', 1077 'secure' => true, 1078 'httpOnly' => false 1079 ) 1080 ); 1081 1082 $result = $response->cookie(); 1083 $this->assertEquals($expected, $result); 1084 } 1085 1086/** 1087 * Test CORS 1088 * 1089 * @dataProvider corsData 1090 * @param CakeRequest $request 1091 * @param string $origin 1092 * @param string|array $domains 1093 * @param string|array $methods 1094 * @param string|array $headers 1095 * @param string|bool $expectedOrigin 1096 * @param string|bool $expectedMethods 1097 * @param string|bool $expectedHeaders 1098 * @return void 1099 */ 1100 public function testCors($request, $origin, $domains, $methods, $headers, $expectedOrigin, $expectedMethods = false, $expectedHeaders = false) { 1101 $_SERVER['HTTP_ORIGIN'] = $origin; 1102 1103 $response = $this->getMock('CakeResponse', array('header')); 1104 1105 $method = $response->expects(!$expectedOrigin ? $this->never() : $this->at(0))->method('header'); 1106 $expectedOrigin && $method->with('Access-Control-Allow-Origin', $expectedOrigin ? $expectedOrigin : $this->anything()); 1107 1108 $i = 1; 1109 if ($expectedMethods) { 1110 $response->expects($this->at($i++)) 1111 ->method('header') 1112 ->with('Access-Control-Allow-Methods', $expectedMethods ? $expectedMethods : $this->anything()); 1113 } 1114 if ($expectedHeaders) { 1115 $response->expects($this->at($i++)) 1116 ->method('header') 1117 ->with('Access-Control-Allow-Headers', $expectedHeaders ? $expectedHeaders : $this->anything()); 1118 } 1119 1120 $response->cors($request, $domains, $methods, $headers); 1121 unset($_SERVER['HTTP_ORIGIN']); 1122 } 1123 1124/** 1125 * Feed for testCors 1126 * 1127 * @return array 1128 */ 1129 public function corsData() { 1130 $fooRequest = new CakeRequest(); 1131 1132 $secureRequest = $this->getMock('CakeRequest', array('is')); 1133 $secureRequest->expects($this->any()) 1134 ->method('is') 1135 ->with('ssl') 1136 ->will($this->returnValue(true)); 1137 1138 return array( 1139 array($fooRequest, null, '*', '', '', false, false), 1140 array($fooRequest, 'http://www.foo.com', '*', '', '', '*', false), 1141 array($fooRequest, 'http://www.foo.com', 'www.foo.com', '', '', 'http://www.foo.com', false), 1142 array($fooRequest, 'http://www.foo.com', '*.foo.com', '', '', 'http://www.foo.com', false), 1143 array($fooRequest, 'http://www.foo.com', 'http://*.foo.com', '', '', 'http://www.foo.com', false), 1144 array($fooRequest, 'http://www.foo.com', 'https://www.foo.com', '', '', false, false), 1145 array($fooRequest, 'http://www.foo.com', 'https://*.foo.com', '', '', false, false), 1146 array($fooRequest, 'http://www.foo.com', array('*.bar.com', '*.foo.com'), '', '', 'http://www.foo.com', false), 1147 1148 array($secureRequest, 'https://www.bar.com', 'www.bar.com', '', '', 'https://www.bar.com', false), 1149 array($secureRequest, 'https://www.bar.com', 'http://www.bar.com', '', '', false, false), 1150 array($secureRequest, 'https://www.bar.com', '*.bar.com', '', '', 'https://www.bar.com', false), 1151 1152 array($fooRequest, 'http://www.foo.com', '*', 'GET', '', '*', 'GET'), 1153 array($fooRequest, 'http://www.foo.com', '*.foo.com', 'GET', '', 'http://www.foo.com', 'GET'), 1154 array($fooRequest, 'http://www.foo.com', '*.foo.com', array('GET', 'POST'), '', 'http://www.foo.com', 'GET, POST'), 1155 1156 array($fooRequest, 'http://www.foo.com', '*', '', 'X-CakePHP', '*', false, 'X-CakePHP'), 1157 array($fooRequest, 'http://www.foo.com', '*', '', array('X-CakePHP', 'X-MyApp'), '*', false, 'X-CakePHP, X-MyApp'), 1158 array($fooRequest, 'http://www.foo.com', '*', array('GET', 'OPTIONS'), array('X-CakePHP', 'X-MyApp'), '*', 'GET, OPTIONS', 'X-CakePHP, X-MyApp'), 1159 ); 1160 } 1161 1162/** 1163 * testFileNotFound 1164 * 1165 * @expectedException NotFoundException 1166 * @return void 1167 */ 1168 public function testFileNotFound() { 1169 $response = new CakeResponse(); 1170 $response->file('/some/missing/folder/file.jpg'); 1171 } 1172 1173/** 1174 * test file with ../ 1175 * 1176 * @expectedException NotFoundException 1177 * @expectedExceptionMessage The requested file contains `..` and will not be read. 1178 * @return void 1179 */ 1180 public function testFileWithForwardSlashPathTraversal() { 1181 $response = new CakeResponse(); 1182 $response->file('my/../cat.gif'); 1183 } 1184 1185/** 1186 * test file with ..\ 1187 * 1188 * @expectedException NotFoundException 1189 * @expectedExceptionMessage The requested file contains `..` and will not be read. 1190 * @return void 1191 */ 1192 public function testFileWithBackwardSlashPathTraversal() { 1193 $response = new CakeResponse(); 1194 $response->file('my\..\cat.gif'); 1195 } 1196 1197/** 1198 * Although unlikely, a file may contain dots in its filename. 1199 * This should be allowed, as long as the dots doesn't specify a path (../ or ..\) 1200 * 1201 * @expectedException NotFoundException 1202 * @execptedExceptionMessageRegExp #The requested file .+my/Some..cat.gif was not found or not readable# 1203 * @return void 1204 */ 1205 public function testFileWithDotsInFilename() { 1206 $response = new CakeResponse(); 1207 $response->file('my/Some..cat.gif'); 1208 } 1209 1210/** 1211 * testFile method 1212 * 1213 * @return void 1214 */ 1215 public function testFile() { 1216 $response = $this->getMock('CakeResponse', array( 1217 'header', 1218 'type', 1219 '_sendHeader', 1220 '_setContentType', 1221 '_isActive', 1222 '_clearBuffer', 1223 '_flushBuffer' 1224 )); 1225 1226 $response->expects($this->exactly(1)) 1227 ->method('type') 1228 ->with('css') 1229 ->will($this->returnArgument(0)); 1230 1231 $response->expects($this->at(1)) 1232 ->method('header') 1233 ->with('Accept-Ranges', 'bytes'); 1234 1235 $response->expects($this->at(2)) 1236 ->method('header') 1237 ->with('Content-Length', 38); 1238 1239 $response->expects($this->once())->method('_clearBuffer'); 1240 $response->expects($this->once())->method('_flushBuffer'); 1241 1242 $response->expects($this->exactly(1)) 1243 ->method('_isActive') 1244 ->will($this->returnValue(true)); 1245 1246 $response->file(CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS . 'test_asset.css'); 1247 1248 ob_start(); 1249 $result = $response->send(); 1250 $output = ob_get_clean(); 1251 $this->assertEquals("/* this is the test asset css file */\n", $output); 1252 $this->assertNotSame(false, $result); 1253 } 1254 1255/** 1256 * testFileWithUnknownFileTypeGeneric method 1257 * 1258 * @return void 1259 */ 1260 public function testFileWithUnknownFileTypeGeneric() { 1261 $currentUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null; 1262 $_SERVER['HTTP_USER_AGENT'] = 'Some generic browser'; 1263 1264 $response = $this->getMock('CakeResponse', array( 1265 'header', 1266 'type', 1267 'download', 1268 '_sendHeader', 1269 '_setContentType', 1270 '_isActive', 1271 '_clearBuffer', 1272 '_flushBuffer' 1273 )); 1274 1275 $response->expects($this->exactly(1)) 1276 ->method('type') 1277 ->with('ini') 1278 ->will($this->returnValue(false)); 1279 1280 $response->expects($this->once()) 1281 ->method('download') 1282 ->with('no_section.ini'); 1283 1284 $response->expects($this->at(2)) 1285 ->method('header') 1286 ->with('Content-Transfer-Encoding', 'binary'); 1287 1288 $response->expects($this->at(3)) 1289 ->method('header') 1290 ->with('Accept-Ranges', 'bytes'); 1291 1292 $response->expects($this->at(4)) 1293 ->method('header') 1294 ->with('Content-Length', 35); 1295 1296 $response->expects($this->once())->method('_clearBuffer'); 1297 $response->expects($this->once())->method('_flushBuffer'); 1298 1299 $response->expects($this->exactly(1)) 1300 ->method('_isActive') 1301 ->will($this->returnValue(true)); 1302 1303 $response->file(CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS . 'no_section.ini'); 1304 1305 ob_start(); 1306 $result = $response->send(); 1307 $output = ob_get_clean(); 1308 $this->assertEquals("some_key = some_value\nbool_key = 1\n", $output); 1309 $this->assertNotSame(false, $result); 1310 if ($currentUserAgent !== null) { 1311 $_SERVER['HTTP_USER_AGENT'] = $currentUserAgent; 1312 } 1313 } 1314 1315/** 1316 * testFileWithUnknownFileTypeOpera method 1317 * 1318 * @return void 1319 */ 1320 public function testFileWithUnknownFileTypeOpera() { 1321 $currentUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null; 1322 $_SERVER['HTTP_USER_AGENT'] = 'Opera/9.80 (Windows NT 6.0; U; en) Presto/2.8.99 Version/11.10'; 1323 1324 $response = $this->getMock('CakeResponse', array( 1325 'header', 1326 'type', 1327 'download', 1328 '_sendHeader', 1329 '_setContentType', 1330 '_isActive', 1331 '_clearBuffer', 1332 '_flushBuffer' 1333 )); 1334 1335 $response->expects($this->at(0)) 1336 ->method('type') 1337 ->with('ini') 1338 ->will($this->returnValue(false)); 1339 1340 $response->expects($this->at(1)) 1341 ->method('type') 1342 ->with('application/octet-stream') 1343 ->will($this->returnValue(false)); 1344 1345 $response->expects($this->once()) 1346 ->method('download') 1347 ->with('no_section.ini'); 1348 1349 $response->expects($this->at(3)) 1350 ->method('header') 1351 ->with('Content-Transfer-Encoding', 'binary'); 1352 1353 $response->expects($this->at(4)) 1354 ->method('header') 1355 ->with('Accept-Ranges', 'bytes'); 1356 1357 $response->expects($this->at(5)) 1358 ->method('header') 1359 ->with('Content-Length', 35); 1360 1361 $response->expects($this->once())->method('_clearBuffer'); 1362 $response->expects($this->once())->method('_flushBuffer'); 1363 $response->expects($this->exactly(1)) 1364 ->method('_isActive') 1365 ->will($this->returnValue(true)); 1366 1367 $response->file(CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS . 'no_section.ini'); 1368 1369 ob_start(); 1370 $result = $response->send(); 1371 $output = ob_get_clean(); 1372 $this->assertEquals("some_key = some_value\nbool_key = 1\n", $output); 1373 $this->assertNotSame(false, $result); 1374 if ($currentUserAgent !== null) { 1375 $_SERVER['HTTP_USER_AGENT'] = $currentUserAgent; 1376 } 1377 } 1378 1379/** 1380 * testFileWithUnknownFileTypeIE method 1381 * 1382 * @return void 1383 */ 1384 public function testFileWithUnknownFileTypeIE() { 1385 $currentUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null; 1386 $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; Media Center PC 4.0; SLCC1; .NET CLR 3.0.04320)'; 1387 1388 $response = $this->getMock('CakeResponse', array( 1389 'header', 1390 'type', 1391 'download', 1392 '_sendHeader', 1393 '_setContentType', 1394 '_isActive', 1395 '_clearBuffer', 1396 '_flushBuffer' 1397 )); 1398 1399 $response->expects($this->at(0)) 1400 ->method('type') 1401 ->with('ini') 1402 ->will($this->returnValue(false)); 1403 1404 $response->expects($this->at(1)) 1405 ->method('type') 1406 ->with('application/force-download') 1407 ->will($this->returnValue(false)); 1408 1409 $response->expects($this->once()) 1410 ->method('download') 1411 ->with('config.ini'); 1412 1413 $response->expects($this->at(3)) 1414 ->method('header') 1415 ->with('Content-Transfer-Encoding', 'binary'); 1416 1417 $response->expects($this->at(4)) 1418 ->method('header') 1419 ->with('Accept-Ranges', 'bytes'); 1420 1421 $response->expects($this->at(5)) 1422 ->method('header') 1423 ->with('Content-Length', 35); 1424 1425 $response->expects($this->once())->method('_clearBuffer'); 1426 $response->expects($this->once())->method('_flushBuffer'); 1427 $response->expects($this->exactly(1)) 1428 ->method('_isActive') 1429 ->will($this->returnValue(true)); 1430 1431 $response->file(CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS . 'no_section.ini', array( 1432 'name' => 'config.ini' 1433 )); 1434 1435 ob_start(); 1436 $result = $response->send(); 1437 $output = ob_get_clean(); 1438 $this->assertEquals("some_key = some_value\nbool_key = 1\n", $output); 1439 $this->assertNotSame(false, $result); 1440 if ($currentUserAgent !== null) { 1441 $_SERVER['HTTP_USER_AGENT'] = $currentUserAgent; 1442 } 1443 } 1444/** 1445 * testFileWithUnknownFileNoDownload method 1446 * 1447 * @return void 1448 */ 1449 public function testFileWithUnknownFileNoDownload() { 1450 $currentUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null; 1451 $_SERVER['HTTP_USER_AGENT'] = 'Some generic browser'; 1452 1453 $response = $this->getMock('CakeResponse', array( 1454 'header', 1455 'type', 1456 'download', 1457 '_sendHeader', 1458 '_setContentType', 1459 '_isActive', 1460 '_clearBuffer', 1461 '_flushBuffer' 1462 )); 1463 1464 $response->expects($this->exactly(1)) 1465 ->method('type') 1466 ->with('ini') 1467 ->will($this->returnValue(false)); 1468 1469 $response->expects($this->at(1)) 1470 ->method('header') 1471 ->with('Accept-Ranges', 'bytes'); 1472 1473 $response->expects($this->never()) 1474 ->method('download'); 1475 1476 $response->file(CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS . 'no_section.ini', array( 1477 'download' => false 1478 )); 1479 1480 if ($currentUserAgent !== null) { 1481 $_SERVER['HTTP_USER_AGENT'] = $currentUserAgent; 1482 } 1483 } 1484 1485/** 1486 * testConnectionAbortedOnBuffering method 1487 * 1488 * @return void 1489 */ 1490 public function testConnectionAbortedOnBuffering() { 1491 $response = $this->getMock('CakeResponse', array( 1492 'header', 1493 'type', 1494 'download', 1495 '_sendHeader', 1496 '_setContentType', 1497 '_isActive', 1498 '_clearBuffer', 1499 '_flushBuffer' 1500 )); 1501 1502 $response->expects($this->any()) 1503 ->method('type') 1504 ->with('css') 1505 ->will($this->returnArgument(0)); 1506 1507 $response->expects($this->at(0)) 1508 ->method('_isActive') 1509 ->will($this->returnValue(false)); 1510 1511 $response->expects($this->once())->method('_clearBuffer'); 1512 $response->expects($this->never())->method('_flushBuffer'); 1513 1514 $response->file(CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS . 'test_asset.css'); 1515 1516 $result = $response->send(); 1517 $this->assertNull($result); 1518 } 1519 1520/** 1521 * Test downloading files with UPPERCASE extensions. 1522 * 1523 * @return void 1524 */ 1525 public function testFileUpperExtension() { 1526 $response = $this->getMock('CakeResponse', array( 1527 'header', 1528 'type', 1529 'download', 1530 '_sendHeader', 1531 '_setContentType', 1532 '_isActive', 1533 '_clearBuffer', 1534 '_flushBuffer' 1535 )); 1536 1537 $response->expects($this->any()) 1538 ->method('type') 1539 ->with('jpg') 1540 ->will($this->returnArgument(0)); 1541 1542 $response->expects($this->at(0)) 1543 ->method('_isActive') 1544 ->will($this->returnValue(true)); 1545 1546 $response->file(CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'img' . DS . 'test_2.JPG'); 1547 } 1548 1549/** 1550 * Test downloading files with extension not explicitly set. 1551 * 1552 * @return void 1553 */ 1554 public function testFileExtensionNotSet() { 1555 $response = $this->getMock('CakeResponse', array( 1556 'header', 1557 'type', 1558 'download', 1559 '_sendHeader', 1560 '_setContentType', 1561 '_isActive', 1562 '_clearBuffer', 1563 '_flushBuffer' 1564 )); 1565 1566 $response->expects($this->any()) 1567 ->method('type') 1568 ->with('jpg') 1569 ->will($this->returnArgument(0)); 1570 1571 $response->expects($this->at(0)) 1572 ->method('_isActive') 1573 ->will($this->returnValue(true)); 1574 1575 $response->file(CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'img' . DS . 'test_2.JPG'); 1576 } 1577 1578/** 1579 * A data provider for testing various ranges 1580 * 1581 * @return array 1582 */ 1583 public static function rangeProvider() { 1584 return array( 1585 // suffix-byte-range 1586 array( 1587 'bytes=-25', 25, 'bytes 13-37/38' 1588 ), 1589 1590 array( 1591 'bytes=0-', 38, 'bytes 0-37/38' 1592 ), 1593 array( 1594 'bytes=10-', 28, 'bytes 10-37/38' 1595 ), 1596 array( 1597 'bytes=10-20', 11, 'bytes 10-20/38' 1598 ), 1599 ); 1600 } 1601 1602/** 1603 * Test the various range offset types. 1604 * 1605 * @dataProvider rangeProvider 1606 * @return void 1607 */ 1608 public function testFileRangeOffsets($range, $length, $offsetResponse) { 1609 $_SERVER['HTTP_RANGE'] = $range; 1610 $response = $this->getMock('CakeResponse', array( 1611 'header', 1612 'type', 1613 '_sendHeader', 1614 '_isActive', 1615 '_clearBuffer', 1616 '_flushBuffer' 1617 )); 1618 1619 $response->expects($this->at(1)) 1620 ->method('header') 1621 ->with('Content-Disposition', 'attachment; filename="test_asset.css"'); 1622 1623 $response->expects($this->at(2)) 1624 ->method('header') 1625 ->with('Content-Transfer-Encoding', 'binary'); 1626 1627 $response->expects($this->at(3)) 1628 ->method('header') 1629 ->with('Accept-Ranges', 'bytes'); 1630 1631 $response->expects($this->at(4)) 1632 ->method('header') 1633 ->with(array( 1634 'Content-Length' => $length, 1635 'Content-Range' => $offsetResponse, 1636 )); 1637 1638 $response->expects($this->any()) 1639 ->method('_isActive') 1640 ->will($this->returnValue(true)); 1641 1642 $response->file( 1643 CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS . 'test_asset.css', 1644 array('download' => true) 1645 ); 1646 1647 ob_start(); 1648 $result = $response->send(); 1649 ob_get_clean(); 1650 } 1651 1652/** 1653 * Test fetching ranges from a file. 1654 * 1655 * @return void 1656 */ 1657 public function testFileRange() { 1658 $_SERVER['HTTP_RANGE'] = 'bytes=8-25'; 1659 $response = $this->getMock('CakeResponse', array( 1660 'header', 1661 'type', 1662 '_sendHeader', 1663 '_setContentType', 1664 '_isActive', 1665 '_clearBuffer', 1666 '_flushBuffer' 1667 )); 1668 1669 $response->expects($this->exactly(1)) 1670 ->method('type') 1671 ->with('css') 1672 ->will($this->returnArgument(0)); 1673 1674 $response->expects($this->at(1)) 1675 ->method('header') 1676 ->with('Content-Disposition', 'attachment; filename="test_asset.css"'); 1677 1678 $response->expects($this->at(2)) 1679 ->method('header') 1680 ->with('Content-Transfer-Encoding', 'binary'); 1681 1682 $response->expects($this->at(3)) 1683 ->method('header') 1684 ->with('Accept-Ranges', 'bytes'); 1685 1686 $response->expects($this->at(4)) 1687 ->method('header') 1688 ->with(array( 1689 'Content-Length' => 18, 1690 'Content-Range' => 'bytes 8-25/38', 1691 )); 1692 1693 $response->expects($this->once())->method('_clearBuffer'); 1694 1695 $response->expects($this->any()) 1696 ->method('_isActive') 1697 ->will($this->returnValue(true)); 1698 1699 $response->file( 1700 CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS . 'test_asset.css', 1701 array('download' => true) 1702 ); 1703 1704 ob_start(); 1705 $result = $response->send(); 1706 $output = ob_get_clean(); 1707 $this->assertEquals(206, $response->statusCode()); 1708 $this->assertEquals("is the test asset ", $output); 1709 $this->assertNotSame(false, $result); 1710 } 1711 1712/** 1713 * Provider for invalid range header values. 1714 * 1715 * @return array 1716 */ 1717 public function invalidFileRangeProvider() { 1718 return array( 1719 // malformed range 1720 array( 1721 'bytes=0,38' 1722 ), 1723 1724 // malformed punctuation 1725 array( 1726 'bytes: 0 - 32' 1727 ), 1728 array( 1729 'garbage: poo - poo' 1730 ), 1731 ); 1732 } 1733 1734/** 1735 * Test invalid file ranges. 1736 * 1737 * @dataProvider invalidFileRangeProvider 1738 * @return void 1739 */ 1740 public function testFileRangeInvalid($range) { 1741 $_SERVER['HTTP_RANGE'] = $range; 1742 $response = $this->getMock('CakeResponse', array( 1743 '_sendHeader', 1744 '_isActive', 1745 )); 1746 1747 $response->file( 1748 CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS . 'test_asset.css', 1749 array('download' => true) 1750 ); 1751 1752 $expected = array( 1753 'Content-Disposition' => 'attachment; filename="test_asset.css"', 1754 'Content-Transfer-Encoding' => 'binary', 1755 'Accept-Ranges' => 'bytes', 1756 'Content-Range' => 'bytes 0-37/38', 1757 'Content-Length' => 38, 1758 ); 1759 $this->assertEquals($expected, $response->header()); 1760 } 1761 1762/** 1763 * Test backwards file range 1764 * 1765 * @return void 1766 */ 1767 public function testFileRangeReversed() { 1768 $_SERVER['HTTP_RANGE'] = 'bytes=30-5'; 1769 $response = $this->getMock('CakeResponse', array( 1770 '_sendHeader', 1771 '_isActive', 1772 )); 1773 1774 $response->file( 1775 CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS . 'test_asset.css', 1776 array('download' => true) 1777 ); 1778 1779 $expected = array( 1780 'Content-Disposition' => 'attachment; filename="test_asset.css"', 1781 'Content-Transfer-Encoding' => 'binary', 1782 'Accept-Ranges' => 'bytes', 1783 'Content-Range' => 'bytes 0-37/38', 1784 ); 1785 $this->assertEquals($expected, $response->header()); 1786 $this->assertEquals(416, $response->statusCode()); 1787 } 1788 1789/** 1790 * testFileRangeOffsetsNoDownload method 1791 * 1792 * @dataProvider rangeProvider 1793 * @return void 1794 */ 1795 public function testFileRangeOffsetsNoDownload($range, $length, $offsetResponse) { 1796 $_SERVER['HTTP_RANGE'] = $range; 1797 $response = $this->getMock('CakeResponse', array( 1798 'header', 1799 'type', 1800 '_sendHeader', 1801 '_isActive', 1802 '_clearBuffer', 1803 '_flushBuffer' 1804 )); 1805 1806 $response->expects($this->at(1)) 1807 ->method('header') 1808 ->with('Accept-Ranges', 'bytes'); 1809 1810 $response->expects($this->at(2)) 1811 ->method('header') 1812 ->with(array( 1813 'Content-Length' => $length, 1814 'Content-Range' => $offsetResponse, 1815 )); 1816 1817 $response->expects($this->any()) 1818 ->method('_isActive') 1819 ->will($this->returnValue(true)); 1820 1821 $response->file( 1822 CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS . 'test_asset.css', 1823 array('download' => false) 1824 ); 1825 1826 ob_start(); 1827 $response->send(); 1828 ob_get_clean(); 1829 } 1830 1831/** 1832 * testFileRangeNoDownload method 1833 * 1834 * @return void 1835 */ 1836 public function testFileRangeNoDownload() { 1837 $_SERVER['HTTP_RANGE'] = 'bytes=8-25'; 1838 $response = $this->getMock('CakeResponse', array( 1839 'header', 1840 'type', 1841 '_sendHeader', 1842 '_setContentType', 1843 '_isActive', 1844 '_clearBuffer', 1845 '_flushBuffer' 1846 )); 1847 1848 $response->expects($this->exactly(1)) 1849 ->method('type') 1850 ->with('css') 1851 ->will($this->returnArgument(0)); 1852 1853 $response->expects($this->at(1)) 1854 ->method('header') 1855 ->with('Accept-Ranges', 'bytes'); 1856 1857 $response->expects($this->at(2)) 1858 ->method('header') 1859 ->with(array( 1860 'Content-Length' => 18, 1861 'Content-Range' => 'bytes 8-25/38', 1862 )); 1863 1864 $response->expects($this->once())->method('_clearBuffer'); 1865 1866 $response->expects($this->any()) 1867 ->method('_isActive') 1868 ->will($this->returnValue(true)); 1869 1870 $response->file( 1871 CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS . 'test_asset.css', 1872 array('download' => false) 1873 ); 1874 1875 ob_start(); 1876 $result = $response->send(); 1877 $output = ob_get_clean(); 1878 $this->assertEquals(206, $response->statusCode()); 1879 $this->assertEquals("is the test asset ", $output); 1880 $this->assertNotSame(false, $result); 1881 } 1882 1883/** 1884 * testFileRangeInvalidNoDownload method 1885 * 1886 * @return void 1887 */ 1888 public function testFileRangeInvalidNoDownload() { 1889 $_SERVER['HTTP_RANGE'] = 'bytes=30-2'; 1890 $response = $this->getMock('CakeResponse', array( 1891 'header', 1892 'type', 1893 '_sendHeader', 1894 '_setContentType', 1895 '_isActive', 1896 '_clearBuffer', 1897 '_flushBuffer' 1898 )); 1899 1900 $response->expects($this->at(1)) 1901 ->method('header') 1902 ->with('Accept-Ranges', 'bytes'); 1903 1904 $response->expects($this->at(2)) 1905 ->method('header') 1906 ->with(array( 1907 'Content-Range' => 'bytes 0-37/38', 1908 )); 1909 1910 $response->file( 1911 CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS . 'test_asset.css', 1912 array('download' => false) 1913 ); 1914 1915 $this->assertEquals(416, $response->statusCode()); 1916 $response->send(); 1917 } 1918 1919/** 1920 * Test the location method. 1921 * 1922 * @return void 1923 */ 1924 public function testLocation() { 1925 $response = new CakeResponse(); 1926 $this->assertNull($response->location(), 'No header should be set.'); 1927 $this->assertNull($response->location('http://example.org'), 'Setting a location should return null'); 1928 $this->assertEquals('http://example.org', $response->location(), 'Reading a location should return the value.'); 1929 } 1930 1931} 1932