Browse Source

Implementing RequestHandler::checkNotModified() as a helper for HTTP caching

Jose Lorenzo Rodriguez 14 years ago
parent
commit
dffe84cfbc

+ 29 - 0
lib/Cake/Controller/Component/RequestHandlerComponent.php

@@ -704,4 +704,33 @@ class RequestHandlerComponent extends Component {
 		}
 		$this->_inputTypeMap[$type] = $handler;
 	}
+
+/**
+ * Checks whether a response has not been modified according to the 'If-None-Match' 
+ * (Etags) and 'If-Modified-Since' (last modification date) request 
+ * headers headers. If the response is detected to be not modified, it 
+ * is marked as so accordingly so the client can be informed of that.
+ *
+ * In order to mark a response as not modified, you need to set at least 
+ * the Last-Modified response header or a response etag to be compared 
+ * with the request itself
+ *
+ * @return boolean whether the response was marked as not modified or 
+ * not
+ **/
+	public function checkNotModified() {
+		$etags = preg_split('/\s*,\s*/', $this->request->header('If-None-Match'), null, PREG_SPLIT_NO_EMPTY);
+		$modifiedSince = $this->request->header('If-Modified-Since');
+		if ($responseTag = $this->response->etag()) {
+			$etagMatches = in_array('*', $etags) || in_array($responseTag, $etags);
+		}
+		if ($modifiedSince) {
+			$timeMatches = strtotime($this->response->modified()) == strtotime($modifiedSince);
+		}
+		$notModified = (!isset($etagMatches) || $etagMatches) && (!isset($timeMatches) || $timeMatches);
+		if ($notModified) {
+			$this->response->notModified();
+		}
+		return $notModified;
+	}
 }

+ 105 - 0
lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php

@@ -823,4 +823,109 @@ class RequestHandlerComponentTest extends CakeTestCase {
 	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');
+		$RequestHandler->checkNotModified();
+	}
+
+/**
+ * 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->assertTrue($RequestHandler->checkNotModified());
+	}
+
+/**
+ * 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->assertTrue($RequestHandler->checkNotModified());
+	}
+
+/**
+ * Test checkNotModified method
+ *
+ * @return void
+ **/
+	public function testCheckNotModifiedByEtagAndTimeMismatch() {
+		$_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:01');
+		$RequestHandler->response->expects($this->never())->method('notModified');
+		$this->assertFalse($RequestHandler->checkNotModified());
+	}
+
+/**
+ * Test checkNotModified method
+ *
+ * @return void
+ **/
+	public function testCheckNotModifiedByEtagMismatch() {
+		$_SERVER['HTTP_IF_NONE_MATCH'] = 'W/"something-else", "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->never())->method('notModified');
+		$this->assertFalse($RequestHandler->checkNotModified());
+	}
+
+
+/**
+ * Test checkNotModified method
+ *
+ * @return void
+ **/
+	public function testCheckNotModifiedByTime() {
+		$_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->modified('2012-01-01 00:00:00');
+		$RequestHandler->response->expects($this->once())->method('notModified');
+		$this->assertTrue($RequestHandler->checkNotModified());
+	}
+
+		/**
+ * Test checkNotModified method
+ *
+ * @return void
+ **/
+	public function testCheckNotModifiedNoHints() {
+		$_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->expects($this->never())->method('notModified');
+		$this->assertFalse($RequestHandler->checkNotModified());
+	}
 }