This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTest.php

960 lines
42 KiB
PHP
Raw Normal View History

2010-06-23 20:42:41 +01:00
<?php
/*
* This file is part of the Symfony package.
2010-06-23 20:42:41 +01:00
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Component\HttpKernel\HttpCache;
2010-06-23 20:42:41 +01:00
require_once __DIR__.'/HttpCacheTestCase.php';
2010-06-23 20:42:41 +01:00
class HttpCacheTest extends HttpCacheTestCase
2010-06-23 20:42:41 +01:00
{
public function testPassesOnNonGetHeadRequests()
{
$this->setNextResponse(200);
$this->request('POST', '/');
$this->assertHttpKernelIsCalled();
$this->assertResponseOk();
$this->assertTraceContains('pass');
$this->assertFalse($this->response->headers->has('Age'));
}
public function testInvalidatesOnPostPutDeleteRequests()
{
foreach (array('post', 'put', 'delete') as $method) {
$this->setNextResponse(200);
$this->request($method, '/');
$this->assertHttpKernelIsCalled();
$this->assertResponseOk();
$this->assertTraceContains('invalidate');
$this->assertTraceContains('pass');
}
}
public function testDoesNotCacheWithAuthorizationRequestHeaderAndNonPublicResponse()
{
$this->setNextResponse(200, array('ETag' => '"Foo"'));
$this->request('GET', '/', array('HTTP_AUTHORIZATION' => 'basic foobarbaz'));
$this->assertHttpKernelIsCalled();
$this->assertResponseOk();
$this->assertEquals('private', $this->response->headers->get('Cache-Control'));
$this->assertTraceContains('miss');
$this->assertTraceNotContains('store');
$this->assertFalse($this->response->headers->has('Age'));
}
public function testDoesCacheWithAuthorizationRequestHeaderAndPublicResponse()
{
$this->setNextResponse(200, array('Cache-Control' => 'public', 'ETag' => '"Foo"'));
$this->request('GET', '/', array('HTTP_AUTHORIZATION' => 'basic foobarbaz'));
$this->assertHttpKernelIsCalled();
$this->assertResponseOk();
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$this->assertTrue($this->response->headers->has('Age'));
$this->assertEquals('public', $this->response->headers->get('Cache-Control'));
}
public function testDoesNotCacheWithCookieHeaderAndNonPublicResponse()
{
$this->setNextResponse(200, array('ETag' => '"Foo"'));
$this->request('GET', '/', array(), array('foo' => 'bar'));
$this->assertHttpKernelIsCalled();
$this->assertResponseOk();
$this->assertEquals('private', $this->response->headers->get('Cache-Control'));
$this->assertTraceContains('miss');
$this->assertTraceNotContains('store');
$this->assertFalse($this->response->headers->has('Age'));
}
public function testDoesNotCacheRequestsWithACookieHeader()
{
$this->setNextResponse(200);
$this->request('GET', '/', array(), array('foo' => 'bar'));
$this->assertHttpKernelIsCalled();
$this->assertResponseOk();
$this->assertEquals('private', $this->response->headers->get('Cache-Control'));
$this->assertTraceContains('miss');
$this->assertTraceNotContains('store');
$this->assertFalse($this->response->headers->has('Age'));
}
public function testRespondsWith304WhenIfModifiedSinceMatchesLastModified()
{
$time = new \DateTime();
$this->setNextResponse(200, array('Cache-Control' => 'public', 'Last-Modified' => $time->format(DATE_RFC2822), 'Content-Type' => 'text/plain'), 'Hello World');
2010-06-23 20:42:41 +01:00
$this->request('GET', '/', array('HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822)));
$this->assertHttpKernelIsCalled();
$this->assertEquals(304, $this->response->getStatusCode());
$this->assertFalse($this->response->headers->has('Content-Length'));
$this->assertFalse($this->response->headers->has('Content-Type'));
$this->assertEmpty($this->response->getContent());
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
}
public function testRespondsWith304WhenIfNoneMatchMatchesETag()
{
$this->setNextResponse(200, array('Cache-Control' => 'public', 'ETag' => '12345', 'Content-Type' => 'text/plain'), 'Hello World');
2010-06-23 20:42:41 +01:00
$this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '12345'));
$this->assertHttpKernelIsCalled();
$this->assertEquals(304, $this->response->getStatusCode());
$this->assertFalse($this->response->headers->has('Content-Length'));
$this->assertFalse($this->response->headers->has('Content-Type'));
$this->assertTrue($this->response->headers->has('ETag'));
$this->assertEmpty($this->response->getContent());
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
}
public function testRespondsWith304OnlyIfIfNoneMatchAndIfModifiedSinceBothMatch()
{
$time = new \DateTime();
$this->setNextResponse(200, array(), '', function ($request, $response) use ($time)
{
$response->setStatusCode(200);
$response->headers->set('ETag', '12345');
$response->headers->set('Last-Modified', $time->format(DATE_RFC2822));
$response->headers->set('Content-Type', 'text/plain');
$response->setContent('Hello World');
});
// only ETag matches
$t = \DateTime::createFromFormat('U', time() - 3600);
$this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '12345', 'HTTP_IF_MODIFIED_SINCE' => $t->format(DATE_RFC2822)));
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
// only Last-Modified matches
$this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '1234', 'HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822)));
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
// Both matches
$this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '12345', 'HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822)));
$this->assertHttpKernelIsCalled();
$this->assertEquals(304, $this->response->getStatusCode());
}
public function testValidatesPrivateResponsesCachedOnTheClient()
{
$this->setNextResponse(200, array(), '', function ($request, $response)
{
$etags = preg_split('/\s*,\s*/', $request->headers->get('IF_NONE_MATCH'));
if ($request->cookies->has('authenticated')) {
$response->headers->set('Cache-Control', 'private, no-store');
$response->setETag('"private tag"');
if (in_array('"private tag"', $etags)) {
$response->setStatusCode(304);
} else {
$response->setStatusCode(200);
$response->headers->set('Content-Type', 'text/plain');
$response->setContent('private data');
}
} else {
$response->setETag('"public tag"');
if (in_array('"public tag"', $etags)) {
$response->setStatusCode(304);
} else {
$response->setStatusCode(200);
$response->headers->set('Content-Type', 'text/plain');
$response->setContent('public data');
}
}
});
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('"public tag"', $this->response->headers->get('ETag'));
$this->assertEquals('public data', $this->response->getContent());
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$this->request('GET', '/', array(), array('authenticated' => ''));
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('"private tag"', $this->response->headers->get('ETag'));
$this->assertEquals('private data', $this->response->getContent());
$this->assertTraceContains('stale');
$this->assertTraceContains('invalid');
$this->assertTraceNotContains('store');
}
public function testStoresResponsesWhenNoCacheRequestDirectivePresent()
{
$time = \DateTime::createFromFormat('U', time() + 5);
$this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822)));
2010-06-23 20:42:41 +01:00
$this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache'));
$this->assertHttpKernelIsCalled();
$this->assertTraceContains('store');
$this->assertTrue($this->response->headers->has('Age'));
}
public function testReloadsResponsesWhenCacheHitsButNoCacheRequestDirectivePresentWhenAllowReloadIsSetTrue()
{
$count = 0;
$this->setNextResponse(200, array('Cache-Control' => 'public, max-age=10000'), '', function ($request, $response) use (&$count)
2010-06-23 20:42:41 +01:00
{
++$count;
$response->setContent(1 == $count ? 'Hello World' : 'Goodbye World');
});
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('store');
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('fresh');
$this->cacheConfig['allow_reload'] = true;
$this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache'));
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Goodbye World', $this->response->getContent());
$this->assertTraceContains('reload');
$this->assertTraceContains('store');
}
public function testDoesNotReloadResponsesWhenAllowReloadIsSetFalseDefault()
{
$count = 0;
$this->setNextResponse(200, array('Cache-Control' => 'public, max-age=10000'), '', function ($request, $response) use (&$count)
2010-06-23 20:42:41 +01:00
{
++$count;
$response->setContent(1 == $count ? 'Hello World' : 'Goodbye World');
});
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('store');
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('fresh');
$this->cacheConfig['allow_reload'] = false;
$this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache'));
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceNotContains('reload');
$this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache'));
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceNotContains('reload');
}
public function testRevalidatesFreshCacheEntryWhenMaxAgeRequestDirectiveIsExceededWhenAllowRevalidateOptionIsSetTrue()
{
$count = 0;
$this->setNextResponse(200, array(), '', function ($request, $response) use (&$count)
{
++$count;
$response->headers->set('Cache-Control', 'public, max-age=10000');
2010-06-23 20:42:41 +01:00
$response->setETag($count);
$response->setContent(1 == $count ? 'Hello World' : 'Goodbye World');
});
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('store');
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('fresh');
$this->cacheConfig['allow_revalidate'] = true;
$this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'max-age=0'));
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Goodbye World', $this->response->getContent());
$this->assertTraceContains('stale');
$this->assertTraceContains('invalid');
$this->assertTraceContains('store');
}
public function testDoesNotRevalidateFreshCacheEntryWhenEnableRevalidateOptionIsSetFalseDefault()
{
$count = 0;
$this->setNextResponse(200, array(), '', function ($request, $response) use (&$count)
{
++$count;
$response->headers->set('Cache-Control', 'public, max-age=10000');
2010-06-23 20:42:41 +01:00
$response->setETag($count);
$response->setContent(1 == $count ? 'Hello World' : 'Goodbye World');
});
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('store');
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('fresh');
$this->cacheConfig['allow_revalidate'] = false;
$this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'max-age=0'));
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceNotContains('stale');
$this->assertTraceNotContains('invalid');
$this->assertTraceContains('fresh');
$this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'max-age=0'));
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceNotContains('stale');
$this->assertTraceNotContains('invalid');
$this->assertTraceContains('fresh');
}
public function testFetchesResponseFromBackendWhenCacheMisses()
{
$time = \DateTime::createFromFormat('U', time() + 5);
$this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822)));
2010-06-23 20:42:41 +01:00
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertTraceContains('miss');
$this->assertTrue($this->response->headers->has('Age'));
}
public function testDoesNotCacheSomeStatusCodeResponses()
{
foreach (array_merge(range(201, 202), range(204, 206), range(303, 305), range(400, 403), range(405, 409), range(411, 417), range(500, 505)) as $code) {
$time = \DateTime::createFromFormat('U', time() + 5);
$this->setNextResponse($code, array('Expires' => $time->format(DATE_RFC2822)));
$this->request('GET', '/');
$this->assertEquals($code, $this->response->getStatusCode());
$this->assertTraceNotContains('store');
$this->assertFalse($this->response->headers->has('Age'));
}
}
public function testDoesNotCacheResponsesWithExplicitNoStoreDirective()
{
$time = \DateTime::createFromFormat('U', time() + 5);
$this->setNextResponse(200, array('Expires' => $time->format(DATE_RFC2822), 'Cache-Control' => 'no-store'));
$this->request('GET', '/');
$this->assertTraceNotContains('store');
$this->assertFalse($this->response->headers->has('Age'));
}
public function testDoesNotCacheResponsesWithoutFreshnessInformationOrAValidator()
{
$this->setNextResponse();
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertTraceNotContains('store');
}
public function testCachesResponesWithExplicitNoCacheDirective()
{
$time = \DateTime::createFromFormat('U', time() + 5);
$this->setNextResponse(200, array('Expires' => $time->format(DATE_RFC2822), 'Cache-Control' => 'public, no-cache'));
2010-06-23 20:42:41 +01:00
$this->request('GET', '/');
$this->assertTraceContains('store');
$this->assertTrue($this->response->headers->has('Age'));
}
public function testCachesResponsesWithAnExpirationHeader()
{
$time = \DateTime::createFromFormat('U', time() + 5);
$this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822)));
2010-06-23 20:42:41 +01:00
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertNotNull($this->response->headers->get('Date'));
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$values = $this->getMetaStorageValues();
$this->assertEquals(1, count($values));
}
public function testCachesResponsesWithAMaxAgeDirective()
{
$this->setNextResponse(200, array('Cache-Control' => 'public, max-age=5'));
2010-06-23 20:42:41 +01:00
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertNotNull($this->response->headers->get('Date'));
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$values = $this->getMetaStorageValues();
$this->assertEquals(1, count($values));
}
public function testCachesResponsesWithASMaxAgeDirective()
{
$this->setNextResponse(200, array('Cache-Control' => 's-maxage=5'));
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertNotNull($this->response->headers->get('Date'));
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$values = $this->getMetaStorageValues();
$this->assertEquals(1, count($values));
}
public function testCachesResponsesWithALastModifiedValidatorButNoFreshnessInformation()
{
$time = \DateTime::createFromFormat('U', time());
$this->setNextResponse(200, array('Cache-Control' => 'public', 'Last-Modified' => $time->format(DATE_RFC2822)));
2010-06-23 20:42:41 +01:00
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
}
public function testCachesResponsesWithAnETagValidatorButNoFreshnessInformation()
{
$this->setNextResponse(200, array('Cache-Control' => 'public', 'ETag' => '"123456"'));
2010-06-23 20:42:41 +01:00
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
}
public function testHitsCachedResponsesWithExpiresHeader()
{
$time1 = \DateTime::createFromFormat('U', time() - 5);
$time2 = \DateTime::createFromFormat('U', time() + 5);
$this->setNextResponse(200, array('Cache-Control' => 'public', 'Date' => $time1->format(DATE_RFC2822), 'Expires' => $time2->format(DATE_RFC2822)));
2010-06-23 20:42:41 +01:00
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertNotNull($this->response->headers->get('Date'));
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$this->assertEquals('Hello World', $this->response->getContent());
$this->request('GET', '/');
$this->assertHttpKernelIsNotCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals($this->responses[0]->headers->get('Date'), $this->response->headers->get('Date'));
$this->assertTrue($this->response->headers->get('Age') > 0);
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertTraceContains('fresh');
$this->assertTraceNotContains('store');
$this->assertEquals('Hello World', $this->response->getContent());
}
public function testHitsCachedResponseWithMaxAgeDirective()
{
$time = \DateTime::createFromFormat('U', time() - 5);
$this->setNextResponse(200, array('Date' => $time->format(DATE_RFC2822), 'Cache-Control' => 'public, max-age=10'));
2010-06-23 20:42:41 +01:00
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertNotNull($this->response->headers->get('Date'));
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$this->assertEquals('Hello World', $this->response->getContent());
$this->request('GET', '/');
$this->assertHttpKernelIsNotCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals($this->responses[0]->headers->get('Date'), $this->response->headers->get('Date'));
$this->assertTrue($this->response->headers->get('Age') > 0);
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertTraceContains('fresh');
$this->assertTraceNotContains('store');
$this->assertEquals('Hello World', $this->response->getContent());
}
public function testHitsCachedResponseWithSMaxAgeDirective()
{
$time = \DateTime::createFromFormat('U', time() - 5);
$this->setNextResponse(200, array('Date' => $time->format(DATE_RFC2822), 'Cache-Control' => 's-maxage=10, max-age=0'));
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertNotNull($this->response->headers->get('Date'));
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$this->assertEquals('Hello World', $this->response->getContent());
$this->request('GET', '/');
$this->assertHttpKernelIsNotCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals($this->responses[0]->headers->get('Date'), $this->response->headers->get('Date'));
$this->assertTrue($this->response->headers->get('Age') > 0);
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertTraceContains('fresh');
$this->assertTraceNotContains('store');
$this->assertEquals('Hello World', $this->response->getContent());
}
public function testAssignsDefaultTtlWhenResponseHasNoFreshnessInformation()
{
$this->setNextResponse();
$this->cacheConfig['default_ttl'] = 10;
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertRegExp('/s-maxage=10/', $this->response->headers->get('Cache-Control'));
$this->cacheConfig['default_ttl'] = 10;
$this->request('GET', '/');
$this->assertHttpKernelIsNotCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertTraceContains('fresh');
$this->assertTraceNotContains('store');
$this->assertEquals('Hello World', $this->response->getContent());
}
public function testDoesNotAssignDefaultTtlWhenResponseHasMustRevalidateDirective()
{
$this->setNextResponse(200, array('Cache-Control' => 'must-revalidate'));
$this->cacheConfig['default_ttl'] = 10;
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertTraceContains('miss');
$this->assertTraceNotContains('store');
$this->assertNotRegExp('/s-maxage/', $this->response->headers->get('Cache-Control'));
$this->assertEquals('Hello World', $this->response->getContent());
}
public function testFetchesFullResponseWhenCacheStaleAndNoValidatorsPresent()
{
$time = \DateTime::createFromFormat('U', time() + 5);
$this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822)));
2010-06-23 20:42:41 +01:00
// build initial request
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertNotNull($this->response->headers->get('Date'));
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertNotNull($this->response->headers->get('Age'));
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$this->assertEquals('Hello World', $this->response->getContent());
# go in and play around with the cached metadata directly ...
$values = $this->getMetaStorageValues();
$this->assertEquals(1, count($values));
$tmp = unserialize($values[0]);
$time = \DateTime::createFromFormat('U', time());
$tmp[0][1]['expires'] = $time->format(DATE_RFC2822);
2011-01-31 13:10:53 +00:00
$r = new \ReflectionObject($this->store);
$m = $r->getMethod('save');
$m->setAccessible(true);
$m->invoke($this->store, 'md'.sha1('http://localhost/'), serialize($tmp));
2010-06-23 20:42:41 +01:00
// build subsequent request; should be found but miss due to freshness
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals(0, $this->response->headers->get('Age'));
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertTraceContains('stale');
$this->assertTraceNotContains('fresh');
$this->assertTraceNotContains('miss');
$this->assertTraceContains('store');
$this->assertEquals('Hello World', $this->response->getContent());
}
public function testValidatesCachedResponsesWithLastModifiedAndNoFreshnessInformation()
{
$time = \DateTime::createFromFormat('U', time());
$this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($time)
{
$response->headers->set('Cache-Control', 'public');
2010-06-23 20:42:41 +01:00
$response->headers->set('Last-Modified', $time->format(DATE_RFC2822));
if ($time->format(DATE_RFC2822) == $request->headers->get('IF_MODIFIED_SINCE')) {
$response->setStatusCode(304);
$response->setContent('');
}
});
// build initial request
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertNotNull($this->response->headers->get('Last-Modified'));
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$this->assertTraceNotContains('stale');
// build subsequent request; should be found but miss due to freshness
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertNotNull($this->response->headers->get('Last-Modified'));
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertEquals(0, $this->response->headers->get('Age'));
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('stale');
$this->assertTraceContains('valid');
$this->assertTraceContains('store');
$this->assertTraceNotContains('miss');
}
public function testValidatesCachedResponsesWithETagAndNoFreshnessInformation()
{
$this->setNextResponse(200, array(), 'Hello World', function ($request, $response)
{
$response->headers->set('Cache-Control', 'public');
2010-06-23 20:42:41 +01:00
$response->headers->set('ETag', '"12345"');
if ($response->getETag() == $request->headers->get('IF_NONE_MATCH')) {
$response->setStatusCode(304);
$response->setContent('');
}
});
// build initial request
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertNotNull($this->response->headers->get('ETag'));
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
// build subsequent request; should be found but miss due to freshness
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertNotNull($this->response->headers->get('ETag'));
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
$this->assertEquals(0, $this->response->headers->get('Age'));
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('stale');
$this->assertTraceContains('valid');
$this->assertTraceContains('store');
$this->assertTraceNotContains('miss');
}
public function testReplacesCachedResponsesWhenValidationResultsInNon304Response()
{
$time = \DateTime::createFromFormat('U', time());
$count = 0;
$this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($time, &$count)
{
$response->headers->set('Last-Modified', $time->format(DATE_RFC2822));
switch (++$count) {
case 1:
$response->setContent('first response');
break;
case 2:
$response->setContent('second response');
break;
case 3:
$response->setContent('');
$response->setStatusCode(304);
break;
}
});
// first request should fetch from backend and store in cache
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('first response', $this->response->getContent());
// second request is validated, is invalid, and replaces cached entry
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('second response', $this->response->getContent());
// third response is validated, valid, and returns cached entry
$this->request('GET', '/');
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('second response', $this->response->getContent());
$this->assertEquals(3, $count);
}
public function testPassesHeadRequestsThroughDirectlyOnPass()
{
$that = $this;
$this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($that)
{
$response->setContent('');
$response->setStatusCode(200);
$that->assertEquals('HEAD', $request->getMethod());
});
$this->request('HEAD', '/', array('HTTP_EXPECT' => 'something ...'));
$this->assertHttpKernelIsCalled();
$this->assertEquals('', $this->response->getContent());
}
public function testUsesCacheToRespondToHeadRequestsWhenFresh()
{
$that = $this;
$this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($that)
{
$response->headers->set('Cache-Control', 'public, max-age=10');
2010-06-23 20:42:41 +01:00
$response->setContent('Hello World');
$response->setStatusCode(200);
$that->assertNotEquals('HEAD', $request->getMethod());
});
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals('Hello World', $this->response->getContent());
$this->request('HEAD', '/');
$this->assertHttpKernelIsNotCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('', $this->response->getContent());
$this->assertEquals(strlen('Hello World'), $this->response->headers->get('Content-Length'));
}
public function testInvalidatesCachedResponsesOnPost()
{
$this->setNextResponse(200, array(), 'Hello World', function ($request, $response)
{
if ('GET' == $request->getMethod()) {
$response->setStatusCode(200);
$response->headers->set('Cache-Control', 'public, max-age=500');
$response->setContent('Hello World');
} elseif ('POST' == $request->getMethod()) {
$response->setStatusCode(303);
$response->headers->set('Location', '/');
made some method name changes to have a better coherence throughout the framework When an object has a "main" many relation with related "things" (objects, parameters, ...), the method names are normalized: * get() * set() * all() * replace() * remove() * clear() * isEmpty() * add() * register() * count() * keys() The classes below follow this method naming convention: * BrowserKit\CookieJar -> Cookie * BrowserKit\History -> Request * Console\Application -> Command * Console\Application\Helper\HelperSet -> HelperInterface * DependencyInjection\Container -> services * DependencyInjection\ContainerBuilder -> services * DependencyInjection\ParameterBag\ParameterBag -> parameters * DependencyInjection\ParameterBag\FrozenParameterBag -> parameters * DomCrawler\Form -> FormField * EventDispatcher\Event -> parameters * Form\FieldGroup -> Field * HttpFoundation\HeaderBag -> headers * HttpFoundation\ParameterBag -> parameters * HttpFoundation\Session -> attributes * HttpKernel\Profiler\Profiler -> DataCollectorInterface * Routing\RouteCollection -> Route * Security\Authentication\AuthenticationProviderManager -> AuthenticationProviderInterface * Templating\Engine -> HelperInterface * Translation\MessageCatalogue -> messages The usage of these methods are only allowed when it is clear that there is a main relation: * a CookieJar has many Cookies; * a Container has many services and many parameters (as services is the main relation, we use the naming convention for this relation); * a Console Input has many arguments and many options. There is no "main" relation, and so the naming convention does not apply. For many relations where the convention does not apply, the following methods must be used instead (where XXX is the name of the related thing): * get() -> getXXX() * set() -> setXXX() * all() -> getXXXs() * replace() -> setXXXs() * remove() -> removeXXX() * clear() -> clearXXX() * isEmpty() -> isEmptyXXX() * add() -> addXXX() * register() -> registerXXX() * count() -> countXXX() * keys()
2010-11-23 08:42:19 +00:00
$response->headers->remove('Cache-Control');
2010-06-23 20:42:41 +01:00
$response->setContent('');
}
});
// build initial request to enter into the cache
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
// make sure it is valid
$this->request('GET', '/');
$this->assertHttpKernelIsNotCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('fresh');
// now POST to same URL
$this->request('POST', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals('/', $this->response->headers->get('Location'));
$this->assertTraceContains('invalidate');
$this->assertTraceContains('pass');
$this->assertEquals('', $this->response->getContent());
// now make sure it was actually invalidated
$this->request('GET', '/');
$this->assertHttpKernelIsCalled();
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Hello World', $this->response->getContent());
$this->assertTraceContains('stale');
$this->assertTraceContains('invalid');
$this->assertTraceContains('store');
}
public function testServesFromCacheWhenHeadersMatch()
{
$count = 0;
$this->setNextResponse(200, array('Cache-Control' => 'max-age=10000'), '', function ($request, $response) use (&$count)
{
$response->headers->set('Vary', 'Accept User-Agent Foo');
$response->headers->set('Cache-Control', 'public, max-age=10');
2010-06-23 20:42:41 +01:00
$response->headers->set('X-Response-Count', ++$count);
$response->setContent($request->headers->get('USER_AGENT'));
});
$this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0'));
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Bob/1.0', $this->response->getContent());
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0'));
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Bob/1.0', $this->response->getContent());
$this->assertTraceContains('fresh');
$this->assertTraceNotContains('store');
$this->assertNotNull($this->response->headers->get('X-Content-Digest'));
}
public function testStoresMultipleResponsesWhenHeadersDiffer()
{
$count = 0;
$this->setNextResponse(200, array('Cache-Control' => 'max-age=10000'), '', function ($request, $response) use (&$count)
{
$response->headers->set('Vary', 'Accept User-Agent Foo');
$response->headers->set('Cache-Control', 'public, max-age=10');
2010-06-23 20:42:41 +01:00
$response->headers->set('X-Response-Count', ++$count);
$response->setContent($request->headers->get('USER_AGENT'));
});
$this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0'));
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertEquals('Bob/1.0', $this->response->getContent());
$this->assertEquals(1, $this->response->headers->get('X-Response-Count'));
$this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/2.0'));
$this->assertEquals(200, $this->response->getStatusCode());
$this->assertTraceContains('miss');
$this->assertTraceContains('store');
$this->assertEquals('Bob/2.0', $this->response->getContent());
$this->assertEquals(2, $this->response->headers->get('X-Response-Count'));
$this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0'));
$this->assertTraceContains('fresh');
$this->assertEquals('Bob/1.0', $this->response->getContent());
$this->assertEquals(1, $this->response->headers->get('X-Response-Count'));
$this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/2.0'));
$this->assertTraceContains('fresh');
$this->assertEquals('Bob/2.0', $this->response->getContent());
$this->assertEquals(2, $this->response->headers->get('X-Response-Count'));
$this->request('GET', '/', array('HTTP_USER_AGENT' => 'Bob/2.0'));
$this->assertTraceContains('miss');
$this->assertEquals('Bob/2.0', $this->response->getContent());
$this->assertEquals(3, $this->response->headers->get('X-Response-Count'));
}
public function testShouldCatchExceptions()
{
$this->catchExceptions();
$this->setNextResponse();
$this->request('GET', '/');
$this->assertExceptionsAreCaught();
}
public function testShouldNotCatchExceptions()
{
$this->catchExceptions(false);
$this->setNextResponse();
$this->request('GET', '/');
$this->assertExceptionsAreNotCaught();
}
public function testEsiCacheSendsTheLowestTtl()
{
$responses = array(
array(
'status' => 200,
'body' => '<esi:include src="/foo" /> <esi:include src="/bar" />',
'headers' => array(
'Cache-Control' => 's-maxage=300',
'Surrogate-Control' => 'content="ESI/1.0"',
),
),
array(
'status' => 200,
'body' => 'Hello World!',
'headers' => array('Cache-Control' => 's-maxage=300'),
),
array(
'status' => 200,
'body' => 'My name is Bobby.',
'headers' => array('Cache-Control' => 's-maxage=100'),
),
);
$this->setNextResponses($responses);
$this->request('GET', '/', array(), array(), true);
$this->assertEquals("Hello World! My name is Bobby.", $this->response->getContent());
$this->assertEquals(100, $this->response->getTtl());
}
public function testEsiCacheForceValidation()
{
$responses = array(
array(
'status' => 200,
'body' => '<esi:include src="/foo" /> <esi:include src="/bar" />',
'headers' => array(
'Cache-Control' => 's-maxage=300',
'Surrogate-Control' => 'content="ESI/1.0"',
),
),
array(
'status' => 200,
'body' => 'Hello World!',
'headers' => array('ETag' => 'foobar'),
),
array(
'status' => 200,
'body' => 'My name is Bobby.',
'headers' => array('Cache-Control' => 's-maxage=100'),
),
);
$this->setNextResponses($responses);
$this->request('GET', '/', array(), array(), true);
$this->assertEquals('Hello World! My name is Bobby.', $this->response->getContent());
$this->assertEquals(null, $this->response->getTtl());
$this->assertTrue($this->response->mustRevalidate());
$this->assertTrue($this->response->headers->hasCacheControlDirective('private'));
$this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache'));
}
2010-06-23 20:42:41 +01:00
}