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