8000 [HttpFoundation] HttpCache doesn't refresh stale responses containing an ETag · Issue #19390 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content
[HttpFoundation] HttpCache doesn't refresh stale responses containing an ETag #19390
Closed
@maennchen

Description

@maennchen

Problem

HttpCache doesn't refresh stale responses containing an ETag and is also not able to increment the Age header of the cached response.

Affected Version

I tested the Problem on v3.1, but it seems to be around since a long time.

Scenarios

Scenario with an ETag

1st Call, Cleared Cache:

$ curl -I -XGET http://SOMEHOST/SOMEURL
HTTP/1.1 200 OK
Date: Wed, 20 Jul 2016 13:19:05 GMT
Server: Apache/2.4.18 (Ubuntu)
X-Powered-By: PHP/5.6.20-1+deb.sury.org~trusty+1
Cache-Control: public, s-maxage=60
Vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
Allow: GET
ETag: "6d5b1b67"
X-Content-Digest: enfb4805c151e8bb9dc08ca4464c1184d1cabd0ead565196dfb2b8e1af6b669ee3
Age: 0
X-Symfony-Cache: GET /v1/videos/12/comments: miss, store
Content-Length: 1825
Content-Type: application/hal+json

2nd Call after a few seconds (not the Age which is still 0):

$ curl -I -XGET http://SOMEHOST/SOMEURL
HTTP/1.1 200 OK
Date: Wed, 20 Jul 2016 13:19:10 GMT
Server: Apache/2.4.18 (Ubuntu)
X-Powered-By: PHP/5.6.20-1+deb.sury.org~trusty+1
Cache-Control: public, s-maxage=60
vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
allow: GET
etag: "6d5b1b67"
x-content-digest: enfb4805c151e8bb9dc08ca4464c1184d1cabd0ead565196dfb2b8e1af6b669ee3
Age: 0
X-Symfony-Cache: GET /v1/videos/12/comments: fresh
content-length: 1825
Content-Type: application/hal+json

The cache will not expire and the response will never be refreshed.

Scenario without an ETag

1st Call, Cleared Cache:

$ curl -I -XGET http://SOMEHOST/SOMEURL
HTTP/1.1 200 OK
Date: Wed, 20 Jul 2016 13:18:48 GMT
Server: Apache/2.4.18 (Ubuntu)
X-Powered-By: PHP/5.6.20-1+deb.sury.org~trusty+1
Cache-Control: public, s-maxage=60
Vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
Allow: GET
X-Content-Digest: enfb4805c151e8bb9dc08ca4464c1184d1cabd0ead565196dfb2b8e1af6b669ee3
Age: 0
X-Symfony-Cache: GET /v1/videos/12/comments: miss, store
Content-Length: 1825
Content-Type: application/hal+json

2nd Call after a few seconds (not the Age which increments):

$ curl -I -XGET http://SOMEHOST/SOMEURL
HTTP/1.1 200 OK
Date: Wed, 20 Jul 2016 13:18:57 GMT
Server: Apache/2.4.18 (Ubuntu)
X-Powered-By: PHP/5.6.20-1+deb.sury.org~trusty+1
Cache-Control: public, s-maxage=60
vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
allow: GET
x-content-digest: enfb4805c151e8bb9dc08ca4464c1184d1cabd0ead565196dfb2b8e1af6b669ee3
Age: 5
X-Symfony-Cache: GET /v1/videos/12/comments: fresh
content-length: 1825
Content-Type: application/hal+json

Cause of the Problem

The Date header of the original request is set by accident by calling this function:
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php#L426

if ($response->isCacheable()) {  // isCacheable sets the date if it is not already set.
    $this->store($request, $response);
}

isCacheable will set set date by calling isFresh, getTtl, getAge, getDate. This however does not happen if there is a ETag header.

Solution

Instead of relying on a date header which may be set by accident by a getter method, the date should explicitly be set for all cached responses. I'd propose to add the Date header if not already set in the method HttpCache::store.

https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php#L583

protected function store(Request $request, Response $response)
{
    // Change Start
    if(!$response->headers->has('Date')) {
        $response->setDate(new DateTime());
    }
    // Change End
    try {
        $this->store->write($request, $response);
        $this->record($request, 'store');
        $response->headers->set('Age', $response->getAge());
    } catch (\Exception $e) {
        $this->record($request, 'store-failed');
        if ($this->options['debug']) {
            throw $e;
        }
    }
    // now that the response is cached, release the lock
    $this->store->unlock($request);
}

Workaround for now

$response->setEtag($hash);
// @TODO: Remove as soon as an official PATCH exists for issue https://github.com/symfony/symfony/issues/19390
$response->setDate(new DateTime());

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0