Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[HttpKernel] Call Response->setPrivate() instead of sending raw header() when session is started #25583

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
Dec 31, 2017

Conversation

Toflar
Copy link
Contributor

@Toflar Toflar commented Dec 22, 2017

Q A
Branch? 3.4
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #24988
License MIT
Doc PR -

As described in #24988 I think the current handling of the Cache-Control header set by the NativeSessionStorage causes inconsistent behaviour.

In #24988 @nicolas-grekas states that if you start a session a response should be considered to be private. I do agree with this but up until now, nobody takes care of this on kernel.response.

I think we must always suppress the NativeSessionStorage from generating any headers by default. Otherwise the Cache-Control header never makes it to the Response instance and is thus missed by kernel.response listeners and for example the Symfony HttpCache. So depending on whether you use Symfony's HttpCache or Varnish as a reverse proxy, caching would be handled differently. Varnish would consider the response to be private if you set the php.ini setting session.cache_limiter to nocache (which is default) because it will receive the header. HttpCache would not because the Cache-Control header is not present on the Response. That's inconsistent and may cause confusion or problems when switching proxies.

@nicolas-grekas
Copy link
Member

I think it's more important to be correct on the http boundary than correct internally.
The issue with this approach is that it doesn't follow this principle.
I'd suggest to try harder looking at fixing internal handling instead.

@Toflar Toflar force-pushed the fix-inconsistent-session-handling branch from a87adea to 23724a2 Compare December 22, 2017 15:23
@Toflar
Copy link
Contributor Author

Toflar commented Dec 22, 2017

I don't quite understand. To me the concept of the Response is that it represents exactly what's going to be sent to the client and to me that means it's correct on the http boundary. That's not the case currently.

I'd suggest to try harder looking at fixing internal handling instead.

Can you elaborate on that?

@Toflar Toflar force-pushed the fix-inconsistent-session-handling branch 2 times, most recently from d2c9f7a to 9f41594 Compare December 22, 2017 15:32
@Toflar
Copy link
Contributor Author

Toflar commented Dec 22, 2017

Failing tests do not seem to be related.

@@ -60,7 +60,7 @@ class NativeSessionStorage implements SessionStorageInterface
* ("auto_start", is not supported as it tells PHP to start a session before
* PHP starts to execute user-land code. Setting during runtime has no effect).
*
* cache_limiter, "" (use "0" to prevent headers from being sent entirely).
* cache_limiter, "0"
Copy link
Member

Choose a reason for hiding this comment

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

ok, maybe you have something with this PR :)
I wouldn't change this: the default should be safe when this storage is used without the SaveSessionListener.
instead, I would suggest to change this setting at the same place where SaveSessionListener is registered.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have a different opinion I have to admit :-) To me, Symfony‘s biggest strength has always been the abstraction of Request and Response. Saving the session doesn‘t work automatically, it‘s dependent of the SaveSessionListener. Just the same as making it private should be. I would even go further and check if the session is actually empty and if so, unset the session cookie. That, in my opinion, would be the real clean way.

Copy link
Member

Choose a reason for hiding this comment

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

the thing you're missing is that the session subsystem doesn't know about Request/Response objects, despite being in the same component.
the thing that does the link is SaveSessionListener
which means NativeSessionStorage should have a safe default when used without Req/Resp objects (ie send "private" itself as does native php)
Then when both Session and Req/Resp are used, then SaveSessionListener does the link, and then also the cache_limiter should be set to "0".

I would even go further and check if the session is actually empty and if so, unset the session cookie

isn't that already the case since #24523? or do you mean something else?

Copy link
Member

Choose a reason for hiding this comment

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

I forgot to add that I think I have the same opinion as yours. I'm just trying to help finding a way to make that happen with the current code, without breaking everything :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I understand your point. We could merge the header onto the response object if present but we don't know if it was set by the session handler or not so that doesn't sound like a very safe idea to me.

isn't that already the case since #24523? or do you mean something else?

Yeah you're right, I missed that sorry :)

Copy link
Member

Choose a reason for hiding this comment

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

Here is the patch I mean. Looks enough to me, isn't it?

diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 6f972ce..73f612d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -825,7 +825,7 @@ class FrameworkExtension extends Extension
 
         // session storage
         $container->setAlias('session.storage', $config['storage_id'])->setPrivate(true);
-        $options = array();
+        $options = array('cache_limiter' => '0');
         foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor', 'use_strict_mode') as $key) {
             if (isset($config[$key])) {
                 $options[$key] = $config[$key];
diff --git a/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php
index 36809b5..7a733bd 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php
@@ -53,6 +53,7 @@ class SaveSessionListener implements EventSubscriberInterface
         $session = $event->getRequest()->getSession();
         if ($session && $session->isStarted()) {
             $session->save();
+            $event->getResponse()->setPrivate();
         }
     }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes, that could work out 😄 I'm off for 2 weeks now with no internet so feel free to adjust what's needed if you feel like it cannot wait. Will work on it when I'm back if it can :) Merry X-mas @nicolas-grekas, thanks for all the hard work you put into Symfony 🎉

@nicolas-grekas nicolas-grekas added this to the 3.4 milestone Dec 22, 2017
@nicolas-grekas nicolas-grekas force-pushed the fix-inconsistent-session-handling branch from 9f41594 to 44d8510 Compare December 29, 2017 10:17
@nicolas-grekas nicolas-grekas changed the title Fixed inconsistent cache headers depending on used session storage [HttpKernel] Call Response->setPrivate() instead of sending raw header() when session is started Dec 29, 2017
@nicolas-grekas nicolas-grekas force-pushed the fix-inconsistent-session-handling branch from 44d8510 to dbc1c1c Compare December 29, 2017 10:28
@fabpot
Copy link
Member

fabpot commented Dec 31, 2017

Thank you @Toflar.

@fabpot fabpot merged commit dbc1c1c into symfony:3.4 Dec 31, 2017
fabpot added a commit that referenced this pull request Dec 31, 2017
…g raw header() when session is started (Toflar)

This PR was merged into the 3.4 branch.

Discussion
----------

[HttpKernel] Call Response->setPrivate() instead of sending raw header() when session is started

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #24988
| License       | MIT
| Doc PR        | -

As described in #24988 I think the current handling of the `Cache-Control` header set by the `NativeSessionStorage` causes inconsistent behaviour.

In #24988 @nicolas-grekas states that if you start a session a response should be considered to be private. I do agree with this but up until now, nobody takes care of this on `kernel.response`.

I think we must always suppress the `NativeSessionStorage` from generating any headers by default. Otherwise the `Cache-Control` header never makes it to the `Response` instance and is thus missed by `kernel.response` listeners and for example the Symfony HttpCache. So depending on whether you use Symfony's HttpCache  or Varnish as a reverse proxy, caching would be handled differently.  Varnish would consider the response to be private if you set the php.ini setting `session.cache_limiter` to `nocache` (which is default) because it will receive the header. HttpCache would not because the `Cache-Control` header is not present on the `Response`.  That's inconsistent and may cause confusion or problems when switching proxies.

Commits
-------

dbc1c1c [HttpKernel] Call Response->setPrivate() instead of sending raw header() when session is started
This was referenced Jan 5, 2018
@Tobion
Copy link
Contributor

Tobion commented Jan 6, 2018

Apart from introducing #25698 as bug, this change does not work generically anyway. If someone calls $session->save() manually instead of waiting on the SaveSesssionListener, the headers will again not be part of the symfony response object. It is good practice to save the session as soon as you don't need it anymore. The SaveSessionListener is only meant as fallback to avoid race conditions.

[edit: strike from me (@nicolas-grekas)]

@nicolas-grekas
Copy link
Member

If someone calls $session->save() manually instead of waiting on the SaveSesssionListener [...]

Good catch, I totally missed that.
Should be fixed in #25699

@Toflar Toflar deleted the fix-inconsistent-session-handling branch January 8, 2018 08:13
fabpot added a commit that referenced this pull request Jan 10, 2018
…tting response "private" (nicolas-grekas)

This PR was merged into the 3.4 branch.

Discussion
----------

[HttpKernel] Fix session handling: decouple "save" from setting response "private"

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Fixes #25583 (comment) from @Tobion, and provides extra laziness for the "session" service, related to symfony/recipes#333.

(deps=high failure will be fixed by merging to upper branches.)

Commits
-------

f8727b8 [HttpKernel] Fix session handling: decouple "save" from setting response "private"
symfony-splitter pushed a commit to symfony/http-kernel that referenced this pull request Jan 10, 2018
…tting response "private" (nicolas-grekas)

This PR was merged into the 3.4 branch.

Discussion
----------

[HttpKernel] Fix session handling: decouple "save" from setting response "private"

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Fixes symfony/symfony#25583 (comment) from @Tobion, and provides extra laziness for the "session" service, related to symfony/recipes#333.

(deps=high failure will be fixed by merging to upper branches.)

Commits
-------

f8727b8 [HttpKernel] Fix session handling: decouple "save" from setting response "private"
symfony-splitter pushed a commit to symfony/http-foundation that referenced this pull request Jan 10, 2018
…tting response "private" (nicolas-grekas)

This PR was merged into the 3.4 branch.

Discussion
----------

[HttpKernel] Fix session handling: decouple "save" from setting response "private"

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Fixes symfony/symfony#25583 (comment) from @Tobion, and provides extra laziness for the "session" service, related to symfony/recipes#333.

(deps=high failure will be fixed by merging to upper branches.)

Commits
-------

f8727b8827 [HttpKernel] Fix session handling: decouple "save" from setting response "private"
symfony-splitter pushed a commit to symfony/http-kernel that referenced this pull request Mar 30, 2018
…rivate automatically (Toflar)

This PR was squashed before being merged into the 4.1-dev branch (closes #26681).

Discussion
----------

Allow to easily ask Symfony not to set a response to private automatically

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

This PR is related to the many discussions going on about the latest (Symfony 3.4+) changes regarding the way Symfony handles the session. I think we're almost there now, Symfony 4.1 automatically turns responses into `private` responses once the session is started and it's all done in the `AbstractSessionListener` where the session is also saved.
In other issues/PRs (symfony/symfony#25583, symfony/symfony#25699, symfony/symfony#24988) it was agreed that setting the response to `private` if the session is started is a good default for Symfony. It was also agreed that setting it to `private` does not always make sense because you **can share a response** across sessions, it just requires a more complex caching setup with shared user context etc.
So there must be an easy way to disable this behaviour. Right now it's very hard to do so because what you end up doing is basically decorating the `session_listener` which is very hard because you have to keep track on that over different Symfony versions as the base listener might get additional features etc.

The [FOSCacheBundle](FriendsOfSymfony/FOSHttpCacheBundle#438) is already having this problem, [Contao](contao/core-bundle#1388) has the same issue and there will be probably more. Basically everyone that wants to share a response cache across the session will have to decorate the default listener. That's just too hard, so I came up with this solution. The header is easy. Every project can add that easily. It does not require any extension, configuration or adjustment of any service. It's clean, transparent and has absolutely no impact on "default" Symfony setups.

Would be happy to have some feedback before 4.1 freeze.

Commits
-------

0f36710 Allow to easily ask Symfony not to set a response to private automatically
fabpot added a commit that referenced this pull request Mar 30, 2018
…rivate automatically (Toflar)

This PR was squashed before being merged into the 4.1-dev branch (closes #26681).

Discussion
----------

Allow to easily ask Symfony not to set a response to private automatically

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

This PR is related to the many discussions going on about the latest (Symfony 3.4+) changes regarding the way Symfony handles the session. I think we're almost there now, Symfony 4.1 automatically turns responses into `private` responses once the session is started and it's all done in the `AbstractSessionListener` where the session is also saved.
In other issues/PRs (#25583, #25699, #24988) it was agreed that setting the response to `private` if the session is started is a good default for Symfony. It was also agreed that setting it to `private` does not always make sense because you **can share a response** across sessions, it just requires a more complex caching setup with shared user context etc.
So there must be an easy way to disable this behaviour. Right now it's very hard to do so because what you end up doing is basically decorating the `session_listener` which is very hard because you have to keep track on that over different Symfony versions as the base listener might get additional features etc.

The [FOSCacheBundle](FriendsOfSymfony/FOSHttpCacheBundle#438) is already having this problem, [Contao](contao/core-bundle#1388) has the same issue and there will be probably more. Basically everyone that wants to share a response cache across the session will have to decorate the default listener. That's just too hard, so I came up with this solution. The header is easy. Every project can add that easily. It does not require any extension, configuration or adjustment of any service. It's clean, transparent and has absolutely no impact on "default" Symfony setups.

Would be happy to have some feedback before 4.1 freeze.

Commits
-------

0f36710 Allow to easily ask Symfony not to set a response to private automatically
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