Skip to content

[Response] getMaxAge() returns non-negative integer #48880

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 4, 2023
Merged

[Response] getMaxAge() returns non-negative integer #48880

merged 1 commit into from
Feb 4, 2023

Conversation

pkruithof
Copy link
Contributor

Q A
Branch? 5.4
Bug fix? yes
New feature? no
Deprecations? no
Tickets Refs #48651 (comment)
License MIT
Doc PR

The max-age directive should be a non-negative integer, see MDN:

The max-age=N request directive indicates that the client allows a stored response that is generated on the origin server within N seconds — where N may be any non-negative integer (including 0).

In case the value is negative, it's encouraged to be treated as 0:

In other words, for any max-age value that isn't an integer or isn't non-negative, the caching behavior that's encouraged is to treat the value as if it were 0.

In my case, it lead to a response that was private,no-cache but with an Expires header set in the future. Not every browser handled this inconsistency the same, which eventually led to authentication issues (see linked comment for a more elaborate explanation).

@carsonbot
Copy link

Hey!

Thanks for your PR. You are targeting branch "6.3" but it seems your PR description refers to branch "5.4".
Could you update the PR description or change target branch? This helps core maintainers a lot.

Cheers!

Carsonbot

$response->headers->set('Expires', -1);
$this->assertLessThanOrEqual(time() - 2 * 86400, $response->getExpires()->format('U'));
$this->assertSame(0, $response->getMaxAge());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test was for getMaxAge() however in this case it was testing getExpires(), which seemed wrong.

@pkruithof
Copy link
Contributor Author

There is a failing test for ResponseTest::testGetTtl() where the TTL is asserted to be negative when the response's expiration is in the past. This uses the now changed getMaxAge(). However I'm not sure if I should change the test to assert 0 instead of a negative number, or if getTtl() should be changed to be compliant with the test.

It seems to me that a negative TTL is illogical the same way as a negative max-age. Please advise what to do with this.

@pkruithof pkruithof changed the base branch from 6.3 to 5.4 January 5, 2023 11:21
@fabpot
Copy link
Member

fabpot commented Jan 5, 2023

There is a failing test for ResponseTest::testGetTtl() where the TTL is asserted to be negative when the response's expiration is in the past. This uses the now changed getMaxAge(). However I'm not sure if I should change the test to assert 0 instead of a negative number, or if getTtl() should be changed to be compliant with the test.

It seems to me that a negative TTL is illogical the same way as a negative max-age. Please advise what to do with this.

I supposeI would expect 0.

@pkruithof
Copy link
Contributor Author

There is a failing test for ResponseTest::testGetTtl() where the TTL is asserted to be negative when the response's expiration is in the past. This uses the now changed getMaxAge(). However I'm not sure if I should change the test to assert 0 instead of a negative number, or if getTtl() should be changed to be compliant with the test.
It seems to me that a negative TTL is illogical the same way as a negative max-age. Please advise what to do with this.

I supposeI would expect 0.

Agreed, I've changed the getTtl() method accordingly.

@fabpot fabpot modified the milestones: 6.3, 5.4 Jan 14, 2023
@pkruithof pkruithof changed the title Response::getMaxAge() returns non-negative integer [Response] getMaxAge() returns non-negative integer Jan 17, 2023
@pkruithof
Copy link
Contributor Author

Apologies for the review requests, they were done automatically when I made a rebase mistake.

The tests for 8.0 and 8.1 fail, but I am having trouble finding out why. I did a composer install inside a docker container and ran the tests there but then I got a different (unrelated) failing test, while the test that is failing in the build did pass there. Any help here would be appreciated.

Copy link
Member

@fabpot fabpot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All failing tests seem unrelated to your changes.

@fabpot
Copy link
Member

fabpot commented Jan 18, 2023

Went too fast. This one seems relate (on low-deps):

1) Symfony\Component\HttpKernel\Tests\EventListener\SessionListenerTest::testPrivateResponseMaxAgeIsRespectedIfSessionStarted
Failed asserting that -172800 is identical to 0.

/home/runner/work/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php:609

@OskarStark OskarStark changed the title [Response] getMaxAge() returns non-negative integer [Response] getMaxAge() returns non-negative integer Jan 18, 2023
@pkruithof
Copy link
Contributor Author

I noticed that too, the only way that should be possible is if the s-maxage or max-age directives are negative. I want to debug this but I cannot reproduce the failing test. Here's what I did:

docker run --rm -it -v $PWD:/symfony php:8.1-alpine sh
# install xsl extension, composer and cd to `/symfony`
/symfony # export COMPOSER_ROOT_VERSION=5.4.x-dev
/symfony # export SYMFONY_VERSION=5.4
/symfony # composer update --prefer-lowest

This gave the following problem:

    - Root composer.json requires symfony/runtime 5.4.x-dev, it is satisfiable by symfony/runtime[5.4.x-dev] from composer repo (https://repo.packagist.org) but symfony/runtime[dev-main] from path repo (src/Symfony/Component/Runtime) has higher repository priority. The packages from the higher priority repository do not match your constraint and are therefore not installable. That repository is canonical so the lower priority repo's packages are not installable. See https://getcomposer.org/repoprio for details and assistance.

To get around this I temporarily removed the custom repo path for the runtime component (it is not related in any way to this change). Then it did install the dependencies, and the test runs without issues:

/symfony # ./phpunit --filter SessionListenerTest

PHPUnit 9.5.28 by Sebastian Bergmann and contributors.

Warning:       Your XML configuration validates against a deprecated schema.
Suggestion:    Migrate your XML configuration using "--migrate-configuration"!

Testing 
................................................                  48 / 48 (100%)

Time: 00:03.350, Memory: 295.00 MB

OK (48 tests, 226 assertions)

So I'm not sure what to do now?

@pkruithof
Copy link
Contributor Author

@fabpot could you point me in the right direction to move this PR forward?

@lyrixx
Copy link
Member

lyrixx commented Jan 31, 2023

Wohoo. Thanks @pkruithof I hit the very same bug (not so easy to find ; especially if you have a reverse proxy in front of it (HTTP/1.0 vs HTTP/1.1). Your bug report is excellent. I tested your PR and it fixes my issue too.

Note: in our case, with nginx as a proxy, the max age was negative

@fabpot
Copy link
Member

fabpot commented Feb 2, 2023

@pkruithof The problem is that your change impacts 2 components (which are independent). To fix the issue, you should bump the minimum version of HttpFoundation in the HttpKernel composer.json file. Right now, it's ^5.4|^6.0, this should be bumped to the next patch versions of 5.4, 6.0, 6.1, and 6.2. That will fix the tests.

@fabpot
Copy link
Member

fabpot commented Feb 4, 2023

Thank you @pkruithof.

@fabpot fabpot merged commit 29d73d7 into symfony:5.4 Feb 4, 2023
@pkruithof pkruithof deleted the response-max-age-non-negative branch February 7, 2023 08:47
This was referenced Feb 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants