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

How are reloads mapped to cache modes? #524

Closed
mfalken opened this issue Apr 11, 2017 · 27 comments
Closed

How are reloads mapped to cache modes? #524

mfalken opened this issue Apr 11, 2017 · 27 comments

Comments

@mfalken
Copy link

mfalken commented Apr 11, 2017

We were starting to look at implementing this for service worker FetchEvent.request.cache. Does the spec map reloads like the following?

reload (Cache-Control: max-age=0) -> no-cache
hard-reload (Cache-Control: no-cache) -> reload

In Chrome, "reload" would be F5, Ctrl+R, clicking the reload button, etc. "hard-reload" would be Ctrl+Shift+R.

Is getting "no-cache" cache mode from clicking the reload button intuitive to developers? @yutakahirano @jakearchibald

@yutakahirano
Copy link
Member

cc: @toyoshim

@mnot
Copy link
Member

mnot commented Apr 11, 2017

It'd be great to have some consistency here.

CC: max-age=0 and CC: no-cache have very similar semantics. See http://httpwg.org/specs/rfc7234.html#cache-request-directive. What do you want the difference to be?

@toyoshim
Copy link

Yeah, max-age=0 and no-cache are very similar, but the spec use "MUST NOT" only for no-cache. I think this is the reason why browsers use no-cache for the hard-reload historically.

One confusing point of the Fetch cache mode spec to me is that it defines HTTP-network-or-cache fetch as

  • no-cache appends "max-age=0" if there is no Cache-Control header
  • no-store and reload append "no-cache" if there is no Cache-Control header

no-cache does not append "no-cache" but no-store and reload append "no-cache". I'm afraid that this will make many developers confused.
https://fetch.spec.whatwg.org/#http-network-or-cache-fetch step 14 and 15.

IMHO, one of what developers want to clarify is what cache mode is set when a user triggers reload UI, navigation UI, or when Location.reload() / History.go() / back() / forward() is called. This should be the same value among all browser implementations. Unfortunately, we do not have a spec that clarifies how browsers should behave in HTTP layer for reload and history navigation, IIUC. It would be great if we clarify it, but probably it would not be in this spec, and it isn't a good idea to mention details what kinds of Cache-Control should be set for each cache mode here?

Even if we removed Cache-Control explanations, I think cache mode name should be still consistent with actual implementation, e.g. we may want to swap reload and no-cache?

@toyoshim
Copy link

Let me involve @ehsan who added Cache-Control explanations to the spec.

@annevk
Copy link
Member

annevk commented Apr 11, 2017

This is basically #289, no?

@toyoshim
Copy link

I think we discuss different points.

What @mattto asks in the first question is which cache mode should be observed on SW fetch event to handle requests for page loading, I think. But the point of #289 is if browser should modify "default" to be aligned with the navigation type automatically when user specifies it on fetch().

@annevk
Copy link
Member

annevk commented Apr 11, 2017

Okay, I guess my general question still is whether we should have per-fetch and per-fetch-group (or client) cache modes or just per-fetch. Because something like a reload will affect subresources as well and is therefore more of a global than local policy. A global override, if you will.

@toyoshim
Copy link

Probably, it will be difficult to define per-fetch-group cache mode correctly because fyi, in Chrome it apparently has a per-frame cache policy internally that may match to the concept of per-fetch-group cache mode. But actual cache mode for each request was determined by many conditions, e.g. if the per-frame load type is 'reload', cache mode for the main resource could be no-cache, but for others, it could be default.

Anne, what do you think about the point I asked as follows?

One confusing point of the Fetch cache mode spec to me is that it defines HTTP-network-or-cache fetch as

no-cache appends "max-age=0" if there is no Cache-Control header
no-store and reload append "no-cache" if there is no Cache-Control header

Why don't we have a straight-forward maps that set no-cache for no-cache, and no-store for no-store?
(and probably max-age=0 for reload?)

@toyoshim
Copy link

One note:

I said

Unfortunately, we do not have a spec that clarifies how browsers should behave in HTTP layer for reload and history navigation, IIUC. It would be great if we clarify it, but probably it would not be in this spec

But I changed my mind. Probably, Fetch spec is one good candidate to have such definition.

@annevk
Copy link
Member

annevk commented Apr 12, 2017

Why don't we have a straight-forward maps that set no-cache for no-cache, and no-store for no-store? (and probably max-age=0 for reload?)

Apologies for not addressing that point. I'm not sure. These were added based on #231 and #232 which were filed by @ehsan. Unfortunately the process back then wasn't very thorough. Apologies for that too.

What we need to do to make progress here is:

  1. Figure out what browsers do today.
  2. Figure out what we want and to what extent that's compatible with 1.
  3. Create web-platform-tests.
  4. Update the standard.
  5. File browser bugs.

Help with any of that would be greatly appreciated.

@mnot
Copy link
Member

mnot commented Apr 13, 2017

Quick and dirty testing:

Safari

Upon a "normal" reload, will only request HTML (without conditionals) the first time; hitting reload again refreshes HTML/CSS/JS/IMG with conditionals and CC: max-age=0 on all but HTML.

No apparent way to do a hard reload in Safari.

Safari TP

Upon a "normal" reload, same as Safari, although it seems to skip the first case sometimes.

Upon a "hard" reload ("reload from origin", new option), requests HTML/CSS/JS/IMG, the latter three conditional with CC: no-cache.

Firefox Nightly

Upon a "normal" reload, requests HTML/CSS/JS/IMG conditionally, with CC: max-age=0.

Upon a "hard" reload, requests HTML/CSS/JS/IMG unconditionally, with CC: no-cache.

Chrome Canary

Upon a "normal" reload, only requests HTML, conditionally with CC: max-age=0.

Upon a "hard" reload, requests HTML conditionally with CC: max-age=0, JS/CSS/IMG with CC: no-cache.

Edge

Upon a "normal" reload, requests HTML/CSS/JS/IMG conditionally, with no Cache-Control request headers.

(not sure how to do a hard reload in Edge)

@toyoshim
Copy link

toyoshim commented Apr 13, 2017

Thanks for quick survey, mnot!

How did you test hard reload on Chrome? I think it should set no-cache even for HTML.

If we consider to have a per-fetch-group thing, it would not be an exact cache mode, but something like window.performance.navigation.type? But navigation type to cache mode mapping would not be easy and is different on each implementation today.

It is not 1:1 map, and even not just looking resource type. For instance, Chrome checks if load event is already trigger, check if URL contains query part, etc etc. But we could define a basic rule of per-fetch-group navigation type to per-fetch cache mode, with a note that says browser may optimize cache mode to meet browser specific strategy.

So my rough proposals are

  1. define per-fetch-group navigation type
  2. add "auto" cache mode that counts per-fetch-group in to decide preferable per-fetch cache mode automatically
  3. define basic rule of navigation type to cache mode map that browser would respect on converting "auto" to effective cache mode, or calculating cache mode of requests for page loading. (But, browser would adopt some optimizations here)
  4. swap "reload" and "no-cache" of cache mode

1 and 2 could be optional.

The map of 3 would be

navigation type cache mode
navigate default
reload reload
hard reload no-cache
back forward (GET) force-cache
back forward (POST) only-if-cached

But, we could omit hard reload entry because today we do not standardize hard reload at all, i.e. location.reload() does not take a boolean flag in the standard.

@wanderview
Copy link
Member

Might need @mcmanus here to understand our cache-control header mappings.

I think maybe we should separate this into two questions:

  1. How do RequestCache values map to Request headers sent?
  2. What mode is used when the refresh button is pressed?

I think (1) should be spec'd, but I'm not sure about (2). Browsers seem to want to differentiate on refresh button UX.

Firefox Nightly
Upon a "normal" reload, requests HTML/CSS/JS/IMG conditionally, with CC: max-age=0.

Gecko maps a normal reload to the concept of "validate always":

https://dxr.mozilla.org/mozilla-central/source/docshell/base/nsDocShell.cpp#11480

Which from the current fetch spec language seems to best match "no-cache" RequestCache value:

"Fetch creates a conditional request if there is a response in the HTTP cache and a normal request otherwise. It then updates the HTTP cache with the response"

https://dxr.mozilla.org/mozilla-central/source/netwerk/protocol/http/HttpBaseChannel.cpp#2677

This is then mapped to "max-age=0" for greater H1.1 and newer here:

https://dxr.mozilla.org/mozilla-central/source/netwerk/protocol/http/nsHttpChannel.cpp#913

The comment there is interesting.

Upon a "hard" reload, requests HTML/CSS/JS/IMG unconditionally, with CC: no-cache.

For a hard reload gecko intends this to "bypass the cache":

https://dxr.mozilla.org/mozilla-central/source/docshell/base/nsDocShell.cpp#11492

From the fetch spec this seems to map to "reload":

"Fetch behaves as if there is no HTTP cache on the way to the network. Ergo, it creates a normal request and updates the HTTP cache with the response. "

https://dxr.mozilla.org/mozilla-central/source/netwerk/protocol/http/HttpBaseChannel.cpp#2675

This is then mapped to "no-cache" for all HTTP protocol versions with a similar comment as above:

https://dxr.mozilla.org/mozilla-central/source/netwerk/protocol/http/nsHttpChannel.cpp#899

@yutakahirano
Copy link
Member

yutakahirano commented Apr 17, 2017

I think maybe we should separate this into two questions:

  • How do RequestCache values map to Request headers sent?
  • What mode is used when the refresh button is pressed?
    I think (1) should be spec'd, but I'm not sure about (2). Browsers seem to want to differentiate on refresh button UX.

If (2) should not be specified, I feel using "reload" name is confusing. I expect the cache mode to be "reload" when I hit a reload button (if there is "reload" option). In other words, if the value means "bypass the cache", (setting the compatibility aside) I think changing "reload" to "bypass the cache" would be good.

@kinu
Copy link

kinu commented Apr 17, 2017

+1 not to have "reload" as cache mode names if reloading behavior will not be / should not be spec'ed.
Separately from that it feels it's still convenient to developers if UA exposes some flag (like navigation type) to indicate if a fetch corresponds to the browser's reloading behavior or not, but that probably should be split out from the cache modes.

@mfalken
Copy link
Author

mfalken commented Apr 17, 2017

Just for context, the spec dropped FetchEvent#isReload in favor of Request#cache:
w3c/ServiceWorker#873

So the idea was we didn't need a navigation type in addition to cache mode. Maybe that decision should be revisited, if developers want to know whether a navigation came from a reload and we won't spec what cache mode browsers should use when the reload button is clicked. @jakearchibald

@annevk
Copy link
Member

annevk commented Apr 17, 2017

If the rationale is that we don't want to dictate what UX does, then we shouldn't have isReload either surely.

@wanderview
Copy link
Member

If (2) should not be specified, I feel using "reload" name is confusing. I expect the cache mode to be "reload" when I hit a reload button (if there is "reload" option). In other words, if the value means "bypass the cache", (setting the compatibility aside) I think changing "reload" to "bypass the cache" would be good.

So RequestCache and its reload value can be reasoned about solely via the Fetch API. Is it not reasonable for code to want "reload" the URL its passing to fetch? The defined before seems reasonable for the name:

Fetch behaves as if there is no HTTP cache on the way to the network. Ergo, it creates a normal request and updates the HTTP cache with the response.

Now, we just happen to also expose the value the browser UX triggers when its reload/refresh/whatever button is pushed via the FetchEvent. For a "force reload" UX interaction we use the reload type in Firefox and for a "normal reload" UX interaction we use a different type. Why should the UX button behavior force the naming for all uses of fetch()? The two just seem like different things to me.

Now, I don't care overly much about names normally, but we've been shipping the reload value for a year now:

https://bugzilla.mozilla.org/show_bug.cgi?id=1120715

Usage is probably low, so it could possibly be changed. It would be nice, however, to have a stronger reason. There is cost to changing at this point.

In regards to isReload I think part of the reason it was removed was that it was poorly defined for navigation-vs-subresource loads. For example, some people thought it should be set for all subresources from a reloaded page for all time. Others expected it only to be set on the navigation request, etc. I wouldn't object to restoring isReload if we can sort out those issues. It does seems to provide an additional signal in that the browser can note "this was a UX reload vs someone calling fetch with a RequestCache value".

@wanderview
Copy link
Member

An idea for bringing isReload back:

  1. FetchEvent.isReload only applies to navigation requests.
  2. Add a Client.isReload flag that indicates it was reload.

This would allow ServiceWorker scripts to check if sub-resource requests are coming from a reloaded client if they wish.

I don't think this would force browsers to standardize reload button UX behavior.

@toyoshim
Copy link

From the viewpoint of web developers, the essential question here would be how an installed SW script can handle FetchEvents of page loading in an optimized way, even for reload and history navigation. Of course, SW can work as just a http cache, but wants to do something smarter if the script can recognize navigation types.

Ideally said, if we could have a clear 1:1 map of all kinds of navigations to cache mode, it would work perfectly. But it sounds very hard because current implementation is different from each other, and is not simple. Even if everything can be spec-ed, it would become very hard to write a SW script correctly.

If FetchEvents could have a per-fetch-group navigation type that also represents UX navigations too, it would be easy to control all kinds of navigation in a simple way. Here, FetchEvent.isReload is not enough because it's also important use case to identify back/forward navigations. If the script want to work as a simple http cache, it would rely on cache mode instead of navigation type.

About spec-ing UX behaviors:
It becomes very hard for web developers to construct web sites correctly without knowing how reload or back/forward navigations work in each browser. Especially with a SW installed, it would become harder to control correctly. IMHO, this situation is not healthy for the ecosystem, and would be great if we can have a standard.

Also, in addition to UX navigations, how about JavaScript initiated navigations via Location and History interfaces?

@mfalken
Copy link
Author

mfalken commented Jun 28, 2017

I think I like wanderview's #524 (comment) idea and it seems we could extend to something like FetchEvent.navigationLoadType which could be an enum 'navigate', 'back', 'forward', 'reload', 'hard reload'.

@toyoshim
Copy link

Shall we make it consistent to window.performance.navigation.type if you have no reason?
It defines four types, 'navigate', 'reload', 'back_forward', and 'undefined'.

@mfalken
Copy link
Author

mfalken commented Jun 28, 2017

Yea, seems fine to do that.

@rsamuelklatchko
Copy link

Commenting as a representative of the team making Google Search into a PWA, FetchEvent.navigationLoadType would provide us with the feature we need.

@wanderview
Copy link
Member

I think I like wanderview's #524 (comment) idea and it seems we could extend to something like FetchEvent.navigationLoadType which could be an enum 'navigate', 'back', 'forward', 'reload', 'hard reload'.

Shall we make it consistent to window.performance.navigation.type if you have no reason?
It defines four types, 'navigate', 'reload', 'back_forward', and 'undefined'.

LGTM

@mfalken
Copy link
Author

mfalken commented Jul 13, 2017

Started a thread at w3c/ServiceWorker#1167 for navigationLoadType

@mfalken
Copy link
Author

mfalken commented Jun 4, 2018

This has been obsoleted by Request.isHistoryNavigation, isReloadNavigation etc.

@mfalken mfalken closed this as completed Jun 4, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

8 participants