Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[HttpFoundation] HttpCache doesn't refresh stale responses containing an ETag #19390

Copy link
Copy link
Closed
@maennchen

Description

@maennchen
Issue body actions

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

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.