Skip to content
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

Tasks to fix and improve caching #1389

Closed
8 of 9 tasks
Toflar opened this issue Feb 20, 2018 · 18 comments
Closed
8 of 9 tasks

Tasks to fix and improve caching #1389

Toflar opened this issue Feb 20, 2018 · 18 comments
Assignees

Comments

@Toflar
Copy link
Member

Toflar commented Feb 20, 2018

As pointed out in other issues already, the caching system still needs a lot of love. We are making good progress but there are still a lot of things to do to make Contao become the CMS with the best native caching support out there. We should not forget that our requirements are very complex and it will remain an ongoing topic that will accompany us for another few versions of Contao.
This issue should serve as a central place to keep track of all the TODO's around the HTTP cache.

Bugfixes

Features

  • We must ensure, we're not starting the session if we don't need it. Starting the session implicitly means that a response is private and may not be cached as it may contain personal information. As long as the core still requires stuff like this, we need to force public responses.

    • $_SESSION['FORM_DATA'] is a real killer here because we never unset the information stored in that value. We must introduce a config option in the form settings to disable that (or enable explicitly). It should be removed in version 5.

    • hasPreviousSession() should be added everywhere we access the session to make sure the session is not started if there's no cookie (Input class) (Only start the session if needed to find data in FORM_DATA #1471) @Toflar

  • We need insert tags as fragments so every insert tag can decide for its own, how it would like to be rendered (and set the correct caching headers) ([RFC] InsertTags as fragments #1470) @Toflar

  • Add support for tagging responses so that we can cache stuff "forever" and invalidate using cache tags. (Use toflar/psr6-symfony-http-cache-store manager-bundle#60) @Toflar

@leofeyer
Copy link
Member

leofeyer commented Mar 1, 2018

@contao/developers I need your feedback in order to fix the last open bugfix. These are the headers we used to send:

if (!headers_sent())
{
	if ($intCache > 0 && (\Config::get('cacheMode') == 'both' || \Config::get('cacheMode') == 'browser'))
	{
		header('Cache-Control: private, max-age=' . ($intCache - time()));
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
		header('Expires: ' . gmdate('D, d M Y H:i:s', $intCache) . ' GMT');
	}
	else
	{
		header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
		header('Expires: Fri, 06 Jun 1975 15:10:00 GMT');
	}
}

See https://github.com/contao/core-bundle/pull/576/files#diff-9d1eb5ea5f99817ee33995c01eced477L320

@Toflar Your initial PR only uses setPrivate(), setMaxAge() and setSharedMaxAge(). Which headers do you want me to add?

@Toflar
Copy link
Member Author

Toflar commented Mar 1, 2018

Only these, the rest is irrelevant for what you can configure in the page settings (Last-Modified is less-accurate and a fallback method for working with E-Tag which we don't do either). And Expires is harder to set than max-age but does the same. So we only need

  • Private if client cache only
  • no-cache, no-store for client and server cache in case it was disabled
  • max-age value for client cache
  • s-max-age for server cache

@leofeyer
Copy link
Member

leofeyer commented Mar 1, 2018

Hm, I still don't know exactly what to do. 😄 This is the current code:

global $objPage;

if (($objPage->cache === false || $objPage->cache === 0) && ($objPage->clientCache === false || $objPage->clientCache === 0))
{
	return $response->setPrivate();
}

// Do not cache the response if a user is logged in or the page is protected
// TODO: Add support for proxies so they can vary on member context
if (FE_USER_LOGGED_IN === true || BE_USER_LOGGED_IN === true || $objPage->protected || $this->hasAuthenticatedBackendUser())
{
	return $response->setPrivate();
}

if ($objPage->clientCache > 0)
{
	$response->setMaxAge($objPage->clientCache);
}

if ($objPage->cache > 0)
{
	$response->setSharedMaxAge($objPage->cache);
}

return $response;

The third and fourth if conditions are clear. Do I apply no-cache, no-store in both the first and second if condition?

And what is the difference between $objPage->cache === false and $objPage->cache === 0?

@Toflar
Copy link
Member Author

Toflar commented Mar 1, 2018

And what is the difference between $objPage->cache === false and $objPage->cache === 0?

None.

Do I apply no-cache, no-store in both the first and second if condition?

Jop.

@leofeyer
Copy link
Member

leofeyer commented Mar 1, 2018

giphy

@leofeyer
Copy link
Member

leofeyer commented Mar 1, 2018

So then the first two if conditions are

@@ -376,6 +376,9 @@ class FrontendTemplate extends \Template
 
 		if (($objPage->cache === false || $objPage->cache === 0) && ($objPage->clientCache === false || $objPage->clientCache === 0))
 		{
+			$response->headers->addCacheControlDirective('no-cache');
+			$response->headers->addCacheControlDirective('no-store');
+
 			return $response->setPrivate();
 		}
 
@@ -383,6 +386,9 @@ class FrontendTemplate extends \Template
 		// TODO: Add support for proxies so they can vary on member context
 		if (FE_USER_LOGGED_IN === true || BE_USER_LOGGED_IN === true || $objPage->protected || $this->hasAuthenticatedBackendUser())
 		{
+			$response->headers->addCacheControlDirective('no-cache');
+			$response->headers->addCacheControlDirective('no-store');
+
 			return $response->setPrivate();
 		}

?

@aschempp
Copy link
Member

aschempp commented Mar 5, 2018

Well private does not make sense if it is no-cache, right?

@Toflar
Copy link
Member Author

Toflar commented Mar 5, 2018

It does. It should always be there, no matter the cache directive. They are semantically different and do not provide an answer to the same question.

@Toflar
Copy link
Member Author

Toflar commented Apr 9, 2018

I've created two more PR's. That list is now reduced two one open item (the checkbox in the form settings). @leofeyer are you going to take over that one?

@leofeyer
Copy link
Member

leofeyer commented Apr 9, 2018

Yes.

@leofeyer
Copy link
Member

@Toflar How does a form setting solve our problem again?

First we would have to add a third argument $blnNoSession to all Input::post*() methods. Then we would have to set this argument to true in the Form class.

  1. What about the Widget::getPost() method?
  2. What if someone uses the Input class without the Form class?

@aschempp
Copy link
Member

I don't think that's the case. We just don't write to $_SESSION['FORM_DATA'] when submitting a form. Everything else solves automatically because no session will be started in that case, so neither Widget nor Input will try to use these values.

@leofeyer
Copy link
Member

But Input::findPost() will still execute isset($_SESSION['FORM_DATA'][$strKey]) which will start the session, won't it?

@aschempp
Copy link
Member

Not with #1471

@Toflar
Copy link
Member Author

Toflar commented Apr 11, 2018

What about the Widget::getPost() method?

I guess this is the reason why this session stuff was introduced back 10 years ago. If you submitted the form with errors, I guess thanks to the session the form was not cleared and the values were propagated again so you could only fix what was wrong: I'm not sure at all though but I think it should not rely on the session either. Nowadays we should leverage browser capabilities to do so (if they do not automatically already, otherwise use localstorage or whatever).

What if someone uses the Input class without the Form class?

Then the session is getting started if the cookie is there. We cannot prevent that because of BC reasons. I'd really love to drop it completely but we can't.

@aschempp
Copy link
Member

aschempp commented Apr 11, 2018 via email

@Toflar
Copy link
Member Author

Toflar commented Apr 11, 2018

I think that used to be different in earlier versions. That's maybe why.

leofeyer added a commit that referenced this issue Jun 11, 2018
Description
-----------

See #1389.

Commits
-------

da6bfc5 Auto-clear the session form data (see #1389).
6847201 Fix a comment and add the unit tests.
@leofeyer
Copy link
Member

leofeyer commented Jul 11, 2019

Since the last open issue "insert tags as fragments" is up for discussion again (see contao/contao#555), I think we can eventually close this ticket.

leofeyer added a commit that referenced this issue Mar 27, 2020
Description
-----------

| Q                | A
| -----------------| ---
| Fixed issues     | Fixes #1389
| Docs PR or issue | -

Commits
-------

ffbe06a6 Remove the default preview bar datalist option
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants