Movatterモバイル変換


[0]ホーム

URL:


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

Sign up
Appearance settings

[HttpKernel] Correctly merging cache directives in HttpCache/ResponseCacheStrategy#26532

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
fabpot merged 1 commit intosymfony:3.4fromaschempp:http-header-merge
Feb 25, 2019

Conversation

@aschempp
Copy link
Contributor

@aschemppaschempp commentedMar 14, 2018
edited
Loading

QA
Branch?3.4
Bug fix?yes
New feature?no
BC breaks?no
Deprecations?no
Tests pass?yes
Fixed tickets#26245,#26352,#28872
LicenseMIT
Doc PR-

This PR is a first draft to fix the incorrect merging of private and other cache-related headers that are not meant for the shared cache but the browser (see mentioned issues).

The existing implementation ofHttpFoundation\Response is very much tailored to theHttpCache, for exampleisCacheable returnsfalse if the response isprivate, which is not true for a browser cache. That is why my implementation does not longer use much of the response methods. They are however still used by theHttpCache and we should keep them as-is. FYI, theResponseCacheStrategy doesnot affect the stored data ofHttpCache but is only applied to the result of multiple merged subrequests/ESI responses.

I did read up a lot on RFC2616 as a reference.Section 13.4 gives an overall view of when a response MAY be cached.Section 14.9.1 has more insight into theCache-Control directives.

Here's a summary of the relevant information I applied to the implementation:

  • Unless specifically constrained by a cache-control (section 14.9) directive, a caching system MAY always store a successful response (see section 13.8) as a cache entry, MAY return it without validation if it is fresh, and MAY return it after successful validation.

    A response without cache control headers is totally fine, and it's up to the cache (shared or private) to decide what to do with it. That is why the implementation does not longer setno-cache if noCache-Control headers are present.

  • A response received with a status code of 200, 203, 206, 300, 301 or 410 MAY be stored […] unless a cache-control directive prohibits caching.

    A response received with any other status code (e.g. status codes 302 and 307) MUST NOT be returned […] unless there are cache-control directives or another header(s) that explicitly allow it.

    This is whatResponseCacheStrategy::isUncacheable implements to decide whether a response is not cacheable at all. It differs fromResponse::isCacheable which only returns true if there are actualCache-Control headers.

  • Section 13.2.3: When a response is generated from a cache entry, the cache MUST include a single Age header field in the response with a value equal to the cache entry's current_age.

    That's why the implementationalways adds theAge header. It takes the oldest age of any of the responses as common denominator for the content.

  • Section 14.9.3: If a response includes an s-maxage directive, then for a shared cache (but not for a private cache), the maximum age specified by this directive overrides the maximum age specified by either the max-age directive or the Expires header.

    This effectively means thatmax-age,s-maxage andExpires must all be kept on the response. My implementation assumes that we can only do that if they exist inall of the responses, and then takes the lowest value of any of them. Be aware the implementation might look confusing at first. Due to the fact that theAge header might come from another subresponse than the lowest expiration value, the values are stored relative to the current response date and then re-calculated based on the age header.

The Symfony implementation did not and still does not implement the full RFC. As an example, some of theCache-Control headers (likeprivate andno-cache) MAY actually have a string value, but the implementation only supports boolean. Also,CustomCache-Control headers are currently not merged into the final response.

ToDo/Questions:

  1. Section 13.5.2 specifies that we must add aWarning 214 Transformation applied if we modify the response headers.

  2. Should we add anExpires headers based onmax-age if none is explicitly set in the responses? This would essentially provide the same information asmax-age but with support for HTTP/1.0 proxies/clients.

  3. I'm not sure about the implemented handling of theprivate directive. The directive is currently only added to the final response if it is present in all of the subresponses. This can effectively result in no cache-control directive, which does not tell a shared cache that the response must not be cached. However, adding aprivate might also tell a browser to actually cache it, even though non of the other responses asked for that.

  4. Section 14.9.2: The purpose of theno-store directive is to prevent the inadvertent release or retention of sensitive information […]. Theno-store directive applies to the entire message, and MAY be sent either in a response or in a request. If sent in a request, a cache MUST NOT store any part of either this request or any response to it. If sent in a response, a cache MUST NOT store any part of either this response or the request that elicited it.

    I have not (yet) validated whether theHttpCache implementation respects any of this.

  5. As far as I understand, the current implementation ofResponseHeaderBag::computeCacheControlValue is incorrect.no-cache means a responsemust not be cached by a shared or private cache, which overridesprivate automatically.

  6. The unit tests are still very limited and I want to add plenty more to test and sort-of describe the implementation or assumptions on the RFC.

/cc@nicolas-grekas

#SymfonyConHackday2018

fritzmg reacted with thumbs up emoji
Copy link
Member

@nicolas-grekasnicolas-grekas left a comment
edited
Loading

Choose a reason for hiding this comment

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

Some minor comments to start the review.

My most important comment is that this should be merged as a new feature to me, thus rebased against master.

@fabpot
Copy link
Member

If you have time@mpdude, I would love to get your review on this one.

@mpdude
Copy link
Contributor

mpdude commentedMar 23, 2018
edited
Loading

I'd really like to help here! It's a bit difficult, though, as the description does not directly make clear what we're after.

After reading the linked issues my understanding is that we want to enable theResponseCacheStrategy to deal withprivate responses and come up with a result better thanno-cache, must-revalidate – that is, provide an expiration time.

Before discussing the implementation, can we agree on what the merge policy needs to be?

I have reviewed the existing tests and think that the merge rules could be as follows.

  1. If we have no embedded response, leave the master response alone.
  2. If one of the responses isprivate, the result must beprivate. Otherwise, all responses arepublic.
  3. Validation-type headers (Last-Modified,ETag) need to be removed. Validation cannot reasonably be used on merged responses.
  4. If the result ispublic, compute the finals-maxage by looking for the smallest value among all responses, considerings-maxage,max-age andExpires in this order. If only a single response
    does not provide any of these, the result must useno-cache, must-revalidate.
  5. Try to compute a finalmax-age by looking for the smallest value among all responses, consideringmax-age andExpires in this order. If only a single response does not provide any of these two, we cannot computemax-age.
  6. If the result isprivate and we don't have themax-age, we need to resort tono-cache, must-revalidate.
  7. CalculateAge in a way that the resulting response becomes stale as soon as the lowest TTL of any response expires. [Do we have to considers-maxage ormax-age here?]

Does that make sense?

@aschempp
Copy link
ContributorAuthor

  1. agreed

  2. If not all of the responses are marked aspublic, the final should not havepublic imho. Not having apublic directive is not the same as not having any directive. In the same sense, having oneprivate response (allow browser caching) does not mean all responses can be cached by the browser. Only if all other are eitherpublic orprivate a browser should be allowed to cache the result.FYI: this might not be implemented in my PR.

  3. agreed

  4. Why do we merge multiple header values that are not meant to be the same?

    • s-maxage is for shared caches only
    • max-age is for private caches as well as for shared caches if nos-maxage is defined
    • Expires headers are similar tomax-age, but mostly useful for HTTP/1.0 protocol.Expires can existing in combination withmax-age to control or prohibit caching for HTTP/1.0 servers, especially if they do not support features likemust-revalidate.
  5. same as 3.

  6. Aprivate directive withoutmax-age is perfectly fine. Each cache definition is valid without other. It's up to the cache server to decide on a cache duration if none is given. This assumesall responses are marked as private but all or some of them do not have amax-age.

  7. The age has nothing to do with expiration time.Age is determined by the difference betweenDate header (when the response has been added to cache) andnow. After merging multiple responses, age must represent the oldest response's age imho.

I still don't understand why Symfony generally adds ano-cache, must-revalidation. Quoting from#26245

Ano-cache directive essentially tells all caches (and that includes the Browser, which is a private cache) to no cache the response.

I think you're misreading the RFC you quoted?no-cache means "don't serve this from a cache without revalidation",private means "store in private caches only".no-store is the way of stating "don't keep this in caches".

You're somewhat right with my incorrect interpretation. I wasn't very sure about revalidation, so I looked it up:https://stackoverflow.com/questions/18148884/difference-between-no-cache-and-must-revalidate#19938619
So essentially,no-cache, must-revalidate allows a cache to store it but never to use it without revalidation. And we are removing both possible revalidation options (Etag and Last-Modified, see 2.), which essentially results in a useless waste of storage? Also,no-cache impliesmust-revalidate, so the second directive is not useful either?

@Toflar
Copy link
Contributor

Any progress here? The current implementation breaks client cache headers when working with ESI fragments :(

@nicolas-grekas
Copy link
Member

friendly ping@aschempp

@aschempp
Copy link
ContributorAuthor

aschempp commentedApr 15, 2018
edited
Loading

I have updated the PR to fix some mentioned coding style and implemented point 1 inmy comment. I know there's more, but I think we should discuss the general idea before I finish the implementation.

However, most of the notes in my thoughts in the last comment still stand, so I guess we should also ping@mpdude 😁

Before we discuss the fine details of this though, we should agree or even write down our thoughts on the supposeddefault behavior. Symfonyalways adds cache control headers which I consider strange. Especially the second if-condition means I can have anExpires header but theResponseHeaderBag will add a cache-control directive that "breaks" this for any HTTP/1.1 cache.

According the the RFC, not having aCache-Control header is perfectly fine, but that is not possible with Symfony. Is this behavior considered correct@mpdude /@nicolas-grekas ? And if we want to be conservative (prevent caching if no caching headers are given), I think we should still correctly handle HTTP/1.0 headers?

@aschempp
Copy link
ContributorAuthor

Is there anything I can do to push this forward?

@nicolas-grekas
Copy link
Member

@aschempp would you mind rebasing please?

@aschempp
Copy link
ContributorAuthor

Rebase is done. The change fromaschempp@3d27b59#diff-e5a339b48cec0fa0a4d0c5c4c705f568R75 has been dropped, but I preserved the unit test and they are still passing.

@aschempp
Copy link
ContributorAuthor

I just looked at the disabled unit tests and will follow up with a few changes shortly.

@aschempp
Copy link
ContributorAuthor

Sorry for taking a while. I tried to implement merging ofLast-Modified andEtag but realized this won't work reliably. They now render a response uncachable without additional expiry headers.


One thing I'm not sure about is the default Response behavior. If there are notCache-Control headers, Symfony always addsprivate, must-revalidate.https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php#L309-L331

First of all,private means a browser is allowed to cache it, which is not the same asno Cache-Control provided, ie. probably not cacheable. Secondly,must-revalidate does not make sense because there is never any validation information (Last-Modified andEtag are removed), a validation will never be possible.

@aschempp
Copy link
ContributorAuthor

I have removed two assertions in the HttpCacheTest to fix tests. I'm very well aware that removing assertions to fix a test is not really a practical solution. However, as explained in#26245 (comment), I think the assertions (and Symfony behavior) were wrong in the first place …

@aschempp
Copy link
ContributorAuthor

Any follow-up questions for this PR I need to address?

@nicolas-grekas
Copy link
Member

(could you please rebase on latest 3.4?)

@mpdude
Copy link
Contributor

For me, this PR is too confusing to give meaningful feedback.

Can we somehow simplify this or break it into smaller pieces we could discuss independently?

@aschempp
Copy link
ContributorAuthor

@mpdude did you look at the diff or at the new version of the file directly? What can I do to simplify the changes? I think they are all related, there are no "individual features" that could be split into smaller pieces.

@nicolas-grekas I will rebase as soon as any questions are resolved. Previously all changes in the old file were irrelevant because it is basically a complete rewrite of the class.

@aschempp
Copy link
ContributorAuthor

Is there anything I can do to get this merged at some point?

@fabpot
Copy link
Member

@aschempp Can you rebase this pull request? We cannot merge a pull request with a merge commit. Thank you.

}elseif (null !==$maxAge =min($this->maxAges)) {
$response->setSharedMaxAge($maxAge);
$response->headers->set('Age',$maxAge -min($this->ttls));
// Last-Modified and Etag headers cannot be merged, they render the response uncacheable
Copy link
Member

Choose a reason for hiding this comment

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

This comment does not seem to correspond to the following code, as the following deals with responsesnot having Last-Modified and Etag

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe formulate it as "If an embedded response uses Last-Modified or an Etag, the combined response is not cacheable."

hm, but the code is saying that if we have a success status AND no last modified / etag, it certainlyis cacheable.
should we instead say that if there is either etag or last-modified, we return true?

and to simplify reasoning in this method, i suggest flipping this method over tokeepsFinalResponseCacheable and switch true / false. and invert the bool where we call the method.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

First, the method name was a lengthy discussion and finally was@nicolas-grekas's suggestion.

This step determines that according to RFC, a response isalways cacheable if it has one of the given response codes. Not having any cache-control information doesnot make a response uncacheable, it just does not tell a cache what to do.

Copy link
Contributor

@dbudbuDec 17, 2018
edited
Loading

Choose a reason for hiding this comment

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

ok with the method name. i prefer "positive" naming over negations, but the field is also done that way round so it could add confusion.

to make this robust and easier to understand, how about saying that as soon as there is etag or last-modified, we return true. and move the status check code to the very end. instead ofreturn true,return !\in_array($response->getStatusCode(), array(...)); . that would be more explicit i think.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

That would not be the same!

  • Your suggestion means that a response with eitherETag orLast-ModifiedwillMakeFinalResponseUncacheable. That is not what I (tried to) implement
  • The final response is uncache if
    1. ( it is not a given status codeOR it hasETag orLast-Modified )
    2. AND it does not have any other caching info (likemax-age).

The implementation just works the opposite way.

  1. If it has a given status code and none of the headers, we already know it will not
    …MakeFinalResponseUncacheable
  2. If either it has a different status codeOR it has one of the headers, we must also check for Cache-Control headers

Copy link
Contributor

Choose a reason for hiding this comment

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

right, this is not the same. hm, so if status is 200 and we have no etag/last-modified, we say its ok to cache, even if max-age is set to 0? ah, but then we would still mark the final result with max-age: 0, and same goes for private.

okay, then i agree this is correct, though somewhat counterintuitive. can you mention this in the phpdoc, that cache-control instructions are handled elsewhere?

also, if the fragment has no cache-control header but the master response has max-age: 1 day, would we end up with caching the combined response for a whole day? or should we assume a default max-age when caching something with status 200 and no cache-control instruction? varnish takes 2 minutes in that case, by default...

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

hm, so if status is 200 and we have no etag/last-modified, we say its ok to cache, even if max-age is set to 0?

No, we don't say its ok to cache. We just determine that this response does not make the final response uncacheable. The final response can still have no useful caching info and therefore be not cacheable.

if the fragment has no cache-control header but the master response has max-age: 1 day, would we end up with caching the combined response for a whole day?

There isno difference between ESI fragments and the master response. They are all sent to theadd method, and the result of ALL responses (regardless of their type) is added in theupdate method. So if any - regardless if master or fragment - has no mergeable data, nothing will be added to the final response.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah right, this happens here:https://github.com/symfony/symfony/pull/26532/files#diff-e5a339b48cec0fa0a4d0c5c4c705f568R212 - if one response has no max-age we set that info tofalse.

so this should indeed be fine. maybe put part of this explanation into the phpdoc?

We just determine that this response does not make the final response uncacheable. The final response can still have no useful caching info and therefore be not cacheable.

Copy link
Contributor

Choose a reason for hiding this comment

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

can you port the gist of this discussion into the comment here?

Copy link
Contributor

@dbudbu left a comment

Choose a reason for hiding this comment

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

i think its important that we improve things here.

it is quite confusing to reason about these things, as interactions are quite complicated with the many directives. i think we need to tweak the code to be easier to understand to be more confident we get it right this time.

could you have another iteration to improve the readability of this code?

}elseif (null !==$maxAge =min($this->maxAges)) {
$response->setSharedMaxAge($maxAge);
$response->headers->set('Age',$maxAge -min($this->ttls));
// Last-Modified and Etag headers cannot be merged, they render the response uncacheable
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe formulate it as "If an embedded response uses Last-Modified or an Etag, the combined response is not cacheable."

hm, but the code is saying that if we have a success status AND no last modified / etag, it certainlyis cacheable.
should we instead say that if there is either etag or last-modified, we return true?

and to simplify reasoning in this method, i suggest flipping this method over tokeepsFinalResponseCacheable and switch true / false. and invert the bool where we call the method.

* we have to subtract the age so that the value is normalized for an age of 0.
*
* If the value is lower than the currently stored value, we update the value, to keep a rolling
* minimal value of each instruction.
Copy link
Contributor

Choose a reason for hiding this comment

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

lets also mention that this method is always called and if there is no setting, the default value is null, and we won't set the information on the final response if it was not present on one of the responses.

Copy link
Contributor

Choose a reason for hiding this comment

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

formulation suggestion:

This method is always called, even for responses that do not have the respective instruction intheir `Cache-Control` header (or have no `Cache-Control` header at all). If any response doesnot have an instruction, we do not set that instruction on the final response.

@nicolas-grekas
Copy link
Member

What's the status here?
Please rebase to account for short arrays also.

@aschempp
Copy link
ContributorAuthor

This PR is still ready to me. Be aware that it currently points to 3.4 as a bugfix, so I'm not sure about short arrays?

I have finished everything requested at the SymfonyCon, but we haven't made any more progress since. I'm not sure who's in charge of a final decision on what needs to be completed and actually merge this?

@nicolas-grekas
Copy link
Member

Short arrays have been applied to 3.4 also, that's why a rebase is needed :)

@aschempp
Copy link
ContributorAuthor

Rebased now and updated to short arrays using the php-cs-fixer

@nicolas-grekas
Copy link
Member

Thanks! What about unanswered comments from David?

@aschempp
Copy link
ContributorAuthor

Thanks! What about unanswered comments from David?

You mean this one?#26532 (comment)
I'm not sure how to handle this, or how a different comment would be an improvement…

@dbu
Copy link
Contributor

dbu commentedJan 30, 2019

the open comments are all about improving the phpdoc or doc comments. basically for the places where i misunderstood the code before, because i think the explanations given in the discussion here and in person in lisbon would merit to be in the code file for future reference. some of the interactions are quite intricate (i don't see a way to improve that) and therefore need to be well documented so we still remember why things are as they are in a couple of years.

@nicolas-grekas
Copy link
Member

Any suggestions maybe? :)

Copy link
Member

@nicolas-grekasnicolas-grekas left a comment

Choose a reason for hiding this comment

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

I'm going to trust you on this one :)

aschempp, ausi, dbu, and Toflar reacted with heart emoji
Copy link
Member

@stofstof left a comment

Choose a reason for hiding this comment

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

This implementation is the one we discussed during the SymfonyCon hackday, solving lots of weird cases. Great to see it finally validated 😄

dmaicher reacted with thumbs up emoji
@fabpot
Copy link
Member

Thank you@aschempp.

OskarStark reacted with heart emoji

@fabpotfabpot merged commit893118f intosymfony:3.4Feb 25, 2019
fabpot added a commit that referenced this pull requestFeb 25, 2019
…he/ResponseCacheStrategy (aschempp)This PR was squashed before being merged into the 3.4 branch (closes#26532).Discussion----------[HttpKernel] Correctly merging cache directives in HttpCache/ResponseCacheStrategy| Q             | A| ------------- | ---| Branch?       | 3.4| Bug fix?      | yes| New feature?  | no| BC breaks?    | no| Deprecations? | no| Tests pass?   | yes| Fixed tickets |#26245,#26352,#28872| License       | MIT| Doc PR        | -This PR is a first draft to fix the incorrect merging of private and other cache-related headers that are not meant for the shared cache but the browser (see mentioned issues).The existing implementation of `HttpFoundation\Response` is very much tailored to the `HttpCache`, for example `isCacheable` returns `false` if the response is `private`, which is not true for a browser cache. That is why my implementation does not longer use much of the response methods. They are however still used by the `HttpCache` and we should keep them as-is. FYI, the `ResponseCacheStrategy` does **not** affect the stored data of `HttpCache` but is only applied to the result of multiple merged subrequests/ESI responses.I did read up a lot on RFC2616 as a reference. [Section 13.4](https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4) gives an overall view of when a response MAY be cached. [Section 14.9.1](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1) has more insight into the `Cache-Control` directives.Here's a summary of the relevant information I applied to the implementation: - > Unless specifically constrained by a cache-control (section 14.9) directive, a caching system MAY always store a successful response (see section 13.8) as a cache entry, MAY return it without validation if it is fresh, and MAY return it after successful validation.    A response without cache control headers is totally fine, and it's up to the cache (shared or private) to decide what to do with it. That is why the implementation does not longer set `no-cache` if no `Cache-Control` headers are present. - > A response received with a status code of 200, 203, 206, 300, 301 or 410 MAY be stored […] unless a cache-control directive prohibits caching.    > A response received with any other status code (e.g. status codes 302 and 307) MUST NOT be returned […] unless there are cache-control directives or another header(s) that explicitly allow it.    This is what `ResponseCacheStrategy::isUncacheable` implements to decide whether a response is not cacheable at all. It differs from `Response::isCacheable` which only returns true if there are actual `Cache-Control` headers. - > [Section 13.2.3](https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.3): When a response is generated from a cache entry, the cache MUST include a single Age header field in the response with a value equal to the cache entry's current_age.    That's why the implementation **always** adds the `Age` header. It takes the oldest age of any of the responses as common denominator for the content. - > [Section 14.9.3](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3): If a response includes an s-maxage directive, then for a shared cache (but not for a private cache), the maximum age specified by this directive overrides the maximum age specified by either the max-age directive or the Expires header.    This effectively means that `max-age`, `s-maxage` and `Expires` must all be kept on the response. My implementation assumes that we can only do that if they exist in **all** of the responses, and then takes the lowest value of any of them. Be aware the implementation might look confusing at first. Due to the fact that the `Age` header might come from another subresponse than the lowest expiration value, the values are stored relative to the current response date and then re-calculated based on the age header.The Symfony implementation did not and still does not implement the full RFC. As an example, some of the `Cache-Control` headers (like `private` and `no-cache`) MAY actually have a string value, but the implementation only supports boolean. Also, [Custom `Cache-Control` headers](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.6) are currently not merged into the final response.**ToDo/Questions:** 1. [Section 13.5.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.2) specifies that we must add a [`Warning 214 Transformation applied`](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46) if we modify the response headers. 2. Should we add an `Expires` headers based on `max-age` if none is explicitly set in the responses? This would essentially provide the same information as `max-age` but with support for HTTP/1.0 proxies/clients. 3. I'm not sure about the implemented handling of the `private` directive. The directive is currently only added to the final response if it is present in all of the subresponses. This can effectively result in no cache-control directive, which does not tell a shared cache that the response must not be cached. However, adding a `private` might also tell a browser to actually cache it, even though non of the other responses asked for that. 4. > [Section 14.9.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.2): The purpose of the `no-store` directive is to prevent the inadvertent release or retention of sensitive information […]. The `no-store` directive applies to the entire message, and MAY be sent either in a response or in a request. If sent in a request, a cache MUST NOT store any part of either this request or any response to it. If sent in a response, a cache MUST NOT store any part of either this response or the request that elicited it.    I have not (yet) validated whether the `HttpCache` implementation respects any of this. 5. As far as I understand, the current implementation of [`ResponseHeaderBag::computeCacheControlValue`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php#L313) is incorrect. `no-cache` means a response [must not be cached by a shared or private cache](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1), which overrides `private` automatically. 5. The unit tests are still very limited and I want to add plenty more to test and sort-of describe the implementation or assumptions on the RFC./cc@nicolas-grekas#SymfonyConHackday2018Commits-------893118f [HttpKernel] Correctly merging cache directives in HttpCache/ResponseCacheStrategy
@mpdude
Copy link
Contributor

Great job@aschempp and all others involved!

@aschemppaschempp deleted the http-header-merge branchFebruary 25, 2019 11:50
This was referencedMar 3, 2019
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@nicolas-grekasnicolas-grekasnicolas-grekas approved these changes

@stofstofstof approved these changes

+3 more reviewers

@dbudbudbu left review comments

@ausiausiausi left review comments

@mpdudempdudempdude left review comments

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Projects

None yet

Milestone

3.4

Development

Successfully merging this pull request may close these issues.

10 participants

@aschempp@fabpot@mpdude@Toflar@nicolas-grekas@fritzmg@dbu@ausi@stof@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp