-
Notifications
You must be signed in to change notification settings - Fork 313
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
Allow overlapping scopes #1085
Comments
It is defined in the Match Service Worker Registration algorithm and I think it behaves as you say:
|
Ah, is this already defined then? It seems to be undocumented everywhere else I've looked. |
At the moment, the scope of a SW decides which clients it'll control. Then, fetches from controlled clients triggers fetch events in the controlling service worker. This is how you can get fetch events for urls that are on other origins, despite those urls being clearly out of scope. But, in terms of deciding which service worker gets control, the longest matching scope wins, as @delapuente says. Does this cover your use cases? Do you want an img fetch for |
We'd like a request to Our use case is actually live now, at editor.construct.net. The pattern we've settled on after a few false starts is:
The There's just the problem of serving the file at As I say it's live so these URLs should all work and you can check it out. As far as I can tell it works, so I think our use case is covered already... unless it only works by accident? |
Huh, actually it looks like with this setup, the Is this to spec? According to what @delapuente pointed out, shouldn't the fetch be handled by the SW with longest scope, i.e. at |
It works as I described in #1085 (comment) & it's what the spec says. Here's a demo https://cdn.rawgit.com/jakearchibald/aa15821ef593aa871b241898f926a492/raw/0fe98d28a5c2bddbabc5aafd9260ca0274e82cc6/ (see the console), and here's the code https://gist.github.com/jakearchibald/aa15821ef593aa871b241898f926a492.
Longest matching scope wins control of the page. Requests go through the service worker which controls the page - |
Okay, but I think our use case is slightly different. We have SWs at:
The user navigates their browser to What we want to happen: SW at Is this correct? If so what is the rationale for the SW based on the document rather than the fetch URL to handle the request? It seems to break this versioning use case. |
If the SW at the fetch URL was used, you wouldn't be able to cache anything on another origin, which you want to do in many cases. |
Could there be an opt-in to this kind of behavior? It looks like at the moment we have to write our SW to be a catch-all that handles everything, including caching multiple different versions of the app separately, as well as caching some of the root-level files separately. What we really want is a series of small, independent SWs that are only responsible for one thing, which makes it more modular and easier to write. Currently it seems we have to change the URL structure of our app to get what we want, and oops, we only just figured this out after deploying to production. (Service Worker strikes again!) Other approaches have their own drawbacks too - for example we tried having |
FWIW this is similar to how appcache works. The appcache that controls the page serves the content. And I think preventing caching of any cross-origin resources would have been a huge step backwards. I'll have a think about ways we could support this, but aside from foreign fetch (which is exclusively cross origin) this is the first time I've seen a feature request like this. |
One way could be to figure out if |
I'd be fine with making this same-origin only, but I guess that only solves our use case.
What's the best practice for SW caching multiple versioned URLs? Or is this not something anybody does with SW? If so what is done instead? I'd be interested to learn what others do, since we've found versioning deployments quite tricky so far. I think our key requirement is that old versions are still available at their own URL. Ideally we would then also be able to update a version in-place, such as to back-patch fixes. We've ported from a traditional downloadable desktop app where we have an archive of old releases, and this is something we didn't want to regress on when moving to the web. |
Just thinking about the versioning scheme a bit more, I suppose the URL rewrite with a second download if you visit the other URL is probably the least bad option. |
Closing this, as I think it's been resolved through discussion. Please reopen if I'm wrong! |
What discussion was there and what was the conclusion? |
Oh sorry, I meant discussion in this thread. I thought this was down to a misunderstanding of how appcache worked. |
Actually, this is still important to us, and I'd still like to see it happen. I still run in to nightmarish bugs with overlapping SW scopes (https://bugs.chromium.org/p/chromium/issues/detail?id=789220, closed wontfix, but still no idea what's really going on). It'd be great if it could be better supported. Perhaps the simplest solution is to provide an explicit way for a service worker to hand over a fetch to another SW with a longer scope. E.g. suppose the following two SWs are registered: example.com/sw1.js then sw1.js could do something like this in its fetch handler: self.addEventListener("fetch", event =>
{
// Dispatch to sw2.js if under v2/ path
if (event.request.url.startsWith("v2/"))
return getV2ServiceWorker().handleFetch(event);
// ... (optionally test other paths) ...
// ... handle fetch using this SW's logic ...
}); This provides a simple way to dispatch fetches to other SWs, which helps split a large god-SW in to smaller SWs with limited responsibility. It doesn't sound like it needs to change the existing ways SWs control clients or fetches, it's just a way to use the lowest-level SW as a dispatcher to other SWs. Can we reopen this issue or should I file a new issue? |
From my understanding, #566 relates to allowing multiple/dynamic scopes for a single SW, and #921 relates to allowing multiple SW at the same scope. However I think SW should allow overlapping scopes as a separate case.
What I mean by overlapping scopes is one site could register two SWs at different but nested scopes like this:
https://example.com/root-sw.js
https://example.com/path/path-sw.js
Note
path-sw.js
is within the scope ofroot-sw.js
. I can't find much information on what is actually specified as happening in this situation, but I did see one comment saying "it's implementation defined, don't do it". I also tried it on Chrome and totally couldn't figure out what it was actually doing, but it looked like it picked one SW and just permanently used that. However I think there are valid use-cases around this.In the above example I would like
root-sw.js
gets all fetch events on the origin, except for anything under/path/
, which then goes topath-sw.js
. For example some mappings would be:https://example.com/index.html
->root-sw.js
https://example.com/otherpath/index.html
->root-sw.js
https://example.com/path/index.html
->path-sw.js
https://example.com/path/anotherpath/index.html
->path-sw.js
Use cases
One potential disadvantage of web apps over traditional installed desktop apps is if an update breaks or changes something, users cannot easily roll back to an older version. This can be mitigated by making old versions of a web app available as well.
To me, the obvious way to do that would be something like this:
https://myapp.com/
is the latest version and auto-updateshttps://myapp.com/v1/
would be specifically version 1https://myapp.com/v2/
would be specifically version 2, etc.Then users just use
myapp.com
and if anything goes wrong they can roll back tomyapp.com/v1
, for example.However adding SW-based offline support in this scenario requires that there be SWs at the following scopes:
https://myapp.com/
https://myapp.com/v1/
https://myapp.com/v2/
But note all the versioned URLs fall under the scope of the origin-level SW. Since as far as I can tell SW does not support this case, it forces the URL scheme to be changed. The next best workaround seems to be to use another path for the latest version, e.g.
https://myapp.com/latest/
and now none of the SW scopes overlap. However it seems awkward that we have to change the URL structure of our app solely because of this aspect of SW. Also it's not such a nice URL - it is easier for customers to remember simply
myapp.com
, whereas this way they may have to remember "myapp.com slash something". (Obviously myapp.com can redirect to myapp.com/latest, but the user doesn't necessarily know that will work, and will permanently see myapp.com/latest in their URL bar.)Another use case might be using paths for other services, e.g.:
myapp.com/
serves a web page entry point with a SW that handles notifications and suchmyapp.com/static/
serves static content with a long-term caching SWmyapp.com/api/
serves API content with a short-term caching SWIn this case again the URL structure has to be changed. Alternatively in both cases you could write one master SW that handles all versions or all cases, but that significantly increases the complexity of the SW script. It's nice to have little modular SWs that handle specific paths in specialised ways.
Spec changes
This seems relatively straightforward to specify: fetch events go to the SW with the "nearest" scope. I'd probably better define that as the SW with the longest scope that the fetch event still falls under.
This doesn't need any new API surface and still enables these use cases. It just better defines what currently appears to be murky/undocumented/undefined behavior.
I suspect there may need to be special provisions for updating SWs themselves. For example in the original example, it is unlikely the web developer wants the fetch for
path-sw.js
when registering that service worker to be handled by a previously registeredroot-sw.js
(this seems likely to just cause headaches with stale cached versions). I can't think of any use case for wanting one SW to handle the fetch event for another SW, so it seems reasonable to just say they are an exception.The text was updated successfully, but these errors were encountered: