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/Cache/CacheTest.php
Fabien Potencier b6923dd7b9 changed Cache-Control default value behavior
The PHP native cache limiter feature has been disabled as this is now managed
by the HeaderBag class directly instead (see below.)

The HeaderBag class uses the following rules to define a sensible and
convervative default value for the Response 'Cache-Control' header:

 * If no cache header is defined ('Cache-Control', 'ETag', 'Last-Modified',
   and 'Expires'), 'Cache-Control' is set to 'no-cache';

 * If 'Cache-Control' is empty, its value is set to "private, max-age=0,
   must-revalidate";

 * But if at least one 'Cache-Control' directive is set, and no 'public' or
   'private' directives have been explicitely added, Symfony2 adds the
   'private' directive automatically (except when 's-maxage' is set.)

So, remember to explicitly add the 'public' directive to 'Cache-Control' when
you want shared caches to store your application resources:

    // The Response is private by default
    $response->setEtag($etag);
    $response->setLastModified($date);
    $response->setMaxAge(10);

    // Change the Response to be public
    $response->setPublic();

    // Set cache settings in one call
    $response->setCache(array(
        'etag'          => $etag,
        'last_modified' => $date,
        'max_age'       => 10,
        'public'        => true,
    ));
2010-11-18 17:05:05 +01:00

874 lines
39 KiB
PHP

<?php
/*
* This file is part of the symfony package.
*
* (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\Cache;
require_once __DIR__.'/CacheTestCase.php';
class CacheTest extends CacheTestCase
{
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');
$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');
$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)));
$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)
{
++$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)
{
++$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');
$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');
$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)));
$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'));
$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)));
$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'));
$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)));
$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"'));
$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)));
$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'));
$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)));
// 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);
$this->store->save('md'.sha1('http://localhost:80/'), serialize($tmp));
// 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');
$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');
$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');
$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', '/');
$response->headers->delete('Cache-Control');
$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');
$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');
$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'));
}
}