diff --git a/lib/Cake/Controller/Component/RequestHandlerComponent.php b/lib/Cake/Controller/Component/RequestHandlerComponent.php index 55a98ba9223..63b8ff10ef4 100644 --- a/lib/Cake/Controller/Component/RequestHandlerComponent.php +++ b/lib/Cake/Controller/Component/RequestHandlerComponent.php @@ -95,7 +95,8 @@ class RequestHandlerComponent extends Component { * @param array $settings Array of settings. */ public function __construct(ComponentCollection $collection, $settings = array()) { - parent::__construct($collection, $settings); + $default = array('checkHttpCache' => true); + parent::__construct($collection, $settings + $default); $this->addInputType('xml', array(array($this, 'convertXml'))); $Controller = $collection->getController(); @@ -240,6 +241,23 @@ public function beforeRedirect($controller, $url, $status = null, $exit = true) $this->_stop(); } +/** + * Checks if the response can be considered different according to the request + * headers, and the caching response headers. If it was not modified, then the + * render process is skipped. And the client will get a blank response with a + * "304 Not Modified" header. + * + * @params Controller $controller + * @return boolean false if the render process should be aborted + **/ + public function beforeRender($controller) { + $shouldCheck = $this->settings['checkHttpCache']; + if ($shouldCheck && $this->response->checkNotModified($this->request)) { + $this->response->send(); + return false; + } + } + /** * Returns true if the current HTTP request is Ajax, false otherwise * diff --git a/lib/Cake/Network/CakeResponse.php b/lib/Cake/Network/CakeResponse.php index 3877138ab8d..0bd5e47bf3b 100644 --- a/lib/Cake/Network/CakeResponse.php +++ b/lib/Cake/Network/CakeResponse.php @@ -1034,7 +1034,11 @@ public function checkNotModified(CakeRequest $request) { if ($modifiedSince) { $timeMatches = strtotime($this->modified()) == strtotime($modifiedSince); } - $notModified = (!isset($etagMatches) || $etagMatches) && (!isset($timeMatches) || $timeMatches); + $checks = compact('etagMatches', 'timeMatches'); + if (empty($checks)) { + return false; + } + $notModified = !in_array(false, $checks, true); if ($notModified) { $this->notModified(); } diff --git a/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php b/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php index b24dbf5050d..d36199e63ed 100644 --- a/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php @@ -824,4 +824,59 @@ public function testAddInputTypeException() { $this->RequestHandler->addInputType('csv', array('I am not callable')); } +/** + * Test checkNotModified method + * + * @return void + **/ + public function testCheckNotModifiedByEtagStar() { + $_SERVER['HTTP_IF_NONE_MATCH'] = '*'; + $RequestHandler = $this->getMock('RequestHandlerComponent', array('_stop'), array(&$this->Controller->Components)); + $RequestHandler->response = $this->getMock('CakeResponse', array('notModified')); + $RequestHandler->response->etag('something'); + $RequestHandler->response->expects($this->once())->method('notModified'); + $this->assertFalse($RequestHandler->beforeRender($this->Controller)); + } + +/** + * Test checkNotModified method + * + * @return void + **/ + public function testCheckNotModifiedByEtagExact() { + $_SERVER['HTTP_IF_NONE_MATCH'] = 'W/"something", "other"'; + $RequestHandler = $this->getMock('RequestHandlerComponent', array('_stop'), array(&$this->Controller->Components)); + $RequestHandler->response = $this->getMock('CakeResponse', array('notModified')); + $RequestHandler->response->etag('something', true); + $RequestHandler->response->expects($this->once())->method('notModified'); + $this->assertFalse($RequestHandler->beforeRender($this->Controller)); + } + +/** + * Test checkNotModified method + * + * @return void + **/ + public function testCheckNotModifiedByEtagAndTime() { + $_SERVER['HTTP_IF_NONE_MATCH'] = 'W/"something", "other"'; + $_SERVER['HTTP_IF_MODIFIED_SINCE'] = '2012-01-01 00:00:00'; + $RequestHandler = $this->getMock('RequestHandlerComponent', array('_stop'), array(&$this->Controller->Components)); + $RequestHandler->response = $this->getMock('CakeResponse', array('notModified')); + $RequestHandler->response->etag('something', true); + $RequestHandler->response->modified('2012-01-01 00:00:00'); + $RequestHandler->response->expects($this->once())->method('notModified'); + $this->assertFalse($RequestHandler->beforeRender($this->Controller)); + } + +/** + * Test checkNotModified method + * + * @return void + **/ + public function testCheckNotModifiedNoInfo() { + $RequestHandler = $this->getMock('RequestHandlerComponent', array('_stop'), array(&$this->Controller->Components)); + $RequestHandler->response = $this->getMock('CakeResponse', array('notModified')); + $RequestHandler->response->expects($this->never())->method('notModified'); + $this->assertNull($RequestHandler->beforeRender($this->Controller)); + } }