-
Notifications
You must be signed in to change notification settings - Fork 312
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
Enabling multiple Service Workers for a single scope #921
Comments
Is this mainly for handling fetches? If so, it feels like a same-origin foreign fetch. |
I feel like there have been a number of folks at mozilla who wanted a more composable API for service workers. We've pretty much said that importScripts() is our method for composition, but it kind of sucks due to tight coupling in a single place. On the other hand, though, I don't know if spinning up N worker threads for every network request is a good idea either. |
Yeah, sounds like a big overhead, taking in account this issue: #920 ... unless everyone such worked will live in the same process and all will have the same active (SW is working) time. Which sounds weird too. |
I guess you can think of it that way, yeah.
Can't we run all of them on the same worker thread? At least the use cases I can think of don't need multiple workers running in parallel. One SW yields (by calling |
Just for the info, if one SW is registered at / scope and second on On Jun 30, 2016 11:31 AM, "Yoav Weiss" notifications@github.com wrote:
|
@NekR - I assumed that since requests in different scopes are all in the same context, both will be handled by a single renderer, and therefore the same process. But I may be wrong, as there's a lot I don't know about how SW work. |
Well, this is possible in script today as you noted. You just have to importScripts() the framework you want to use. Since what you are proposing is opt-in as well, I'm not sure how baking this in to the browser is better at this time.
This is completely implementation dependent and could change over time. Ideally you should not rely on details like this. |
I also think there are lots of unknowns that would have to be solved at the cost of increased complexity:
I'm not saying that we should never do this, but I think its still pretty early days in terms of developers figuring out which patterns they want and need. My gut feeling is we should wait to see if a dominant design emerges in the framework ecosystem before baking something into the platform itself. |
The advantage of baking this in is that, at least for the optimization service scenario, only the service SW has to opt in, and the site can remain oblivious to the fact that a SW is running in a lower layer. |
I don't understand what you mean here. The site can be oblivious to whether the SW script uses importScripts() as well. Did you mean the reverse? You can have SW scripts are oblivious to the fact they are running collaboratively with other SW scripts? |
Yeah. Basically, if I'm running such an optimization service, I want to be able to add my own optimization SW, yet I don't want my customers to have to change their existing code to make it work. In current script based solutions, I can potentially do that by overriding most SW related APIs, while hoping that sites won't register a SW using Otherwise, I could use something like serviceworkerware and have to talk to customers and convince them to change their SW for my sake. Neither is ideal. I understand there's added complexity involved in baking this in. There's also a real use-case here, that cannot be entirely resolved in script. |
How do you do this without asking your clients to change their site? Do you inject a script at the CDN level? |
As far as I can tell, this proposal seems to be based on the belief that it's easier for sites/optimization services to drop in a new |
How having 2 SW on ... I am not saying you have to add or not this proposal, just trying to understand. |
Currently the most specific (longest) scope wins. So |
@wanderview I see, thanks. Then this proposal indeed introduces a lot of complexity. |
That's part of the motivation, but a bigger part is the desire to have the code in the two service workers be more independent of each other. As an example, let's say you have a service worker module that does some alternative compression/content-encoding, and you have another service worker module that does caching or offline availability. Currently, these two things need to be combined in the same fetch handler. This is a use case for having layered fetch handlers. |
Yeah. Or add a
Not at all. If an The problem with the current |
This seems false? Like every event, the first-registered listener executes first. If you want to go first, just insert your importScripts at the top, and have your fetch handler call respondWith immediately. |
I think what they want is for each handler to be able to incrementally improve the Response. You still have an ordering problem, though. What if your compression handler runs first, and then their pull-from-cache handler runs second? Some kind of coordination is needed. |
I guess you just have to ask clients to use your performance-proof framework. I rally doubt that you can just drop some JS in their code and make their main thread faster. With multi-SW-per-domain, in some cases, you may even make things worse. Imagine browsers not paying much attention to this feature, not making fixes for it Pri-1 because it's too complex and every change requires many engineer-hours. This is just as an example. Another example could be that not much people will be using it, so some browsers won't effectively support it or just don't implement it at all. |
Let me expand on the use-case @crdumoul described. Let's say example.com has a SW that performs some analysis on the resource's content as well as making sure its static assets are available offline and AwesomeCDN™ is their CDN which uses the magical Flofli compression, a proprietary compression format with great gains. Since Flofli is a proprietary compression format, it has no browser decoding support, and AwesomeCDN relies on SW in order to perform the decoding. As example.com's CDN, AwesomeCDN can inject headers, and with some larger effort HTML tags and scripts into example.com's site. How can AwesomeCDN register its SW so that it would play well with example.com's SW and make sure that it still sees non-Flofli traffic? How can it make sure that its SW sees requests as they were modified by example.com's SW? That it doesn't see requests that got |
If AwesomeCDN is on a different origin you could use foreign fetch for this. example.com's SW runs as normal. An incoming requests to AwesomeCDN origin trigger a ForeignFetchEvent in their SW. This will show the request coming out of example.com's SW and can return a modified Response back to example.com. |
All requests in above scenario go to example.com's own domain. I see how ForeignFetch can do that for some requests that are on a 3rd party domain, but that's not the case here. (hence @jakearchibald's comment that what I'm looking for is same-origin foreign-fetch). |
@yoavweiss Can you accomplish what you want by overriding self.fetch, self.XMLHttpRequest, and self.caches (for cache.add/addAll)? It would seem these would let you intercept network requests and manipulate responses before the main SW script sees them. |
I have been working on a SW script which optimises images on the fly - https://dexecure.com/image.html |
@wanderview overriding It does add complexity (as the code would need to be significantly different if the site has a SW or not, and I'd have to make sure that the overriding happens before the site's SW is run), and I'm having a hard time figuring out how/if a similar model can be used to receive push notifications. (I see how I can intercept them by creating wrappers around the entire SW registration process, which seems be a bit much) Also, IIUC, I'd have to override these values on each |
If you tell your client's to put an You would need to manage state in IDB or Cache API, though. That would be the case no matter how we do this, though. Long term I think the routing API @jakearchibald is talking about over in #920 could probably encompass this use case as well. In addition to falling back from fetch or cache to the service worker, we could add the ability to fallback from one service worker event handler to the next. I'm still not sure we should implement this system yet, though. It would be a big new high level feature and we're still just getting the underlying primitives out now. |
We just encountered this problem again today. |
@inian can you describe how |
Hey @jakearchibald, I am not sure if I got that fully. You are recommending overriding self.fetch in the main HTML document like this? const oldFetch = self.fetch;
self.fetch = function () {
console.log(arguments);
// handle fetch before any SW script sees it
return oldFetch.apply(this, arguments);
}; I tried this and this patched fetch is not the one picked up by the fetch used in the SW. Were you recommending something else? |
I guess @jakearchibald means overriding |
Ah got it. Yes, that should work. Seems super hacky though, doesn't it? |
F2F:
|
Has there been some more work done on this up till now? Last comment is from juli 2016. |
I'm not aware of progress on this front @jakearchibald - was this discussed as part of TPAC's F2F? |
@yoavweiss Meanwhile I've been pointed to Workbox for this kind of functionality. |
@yoavweiss we didn't get onto this. There doesn't seem to be a lot of hunger for it given how much it complicates the model vs other stuff. |
This would be really nice to see. Would make things alot easier in situations where, for an example, the user has an OIDC library which uses a service worker to inject an auth token and an analytics library that uses a service worker for analytics, and maybe a third service worker for offline functionality |
I think the root problem here is that scoping is backwards. A scope should be defined as the requests that a Service Worker should be allowed to handle, rather than where a Service Worker is allowed to run. For example, a developer should be able to register a Service Worker from a different origin that handles requests for that origin. Maybe that Service Worker knows how to speed up certain resources, or knows how to fetch them differently. But, the developer may want to limit that third-party service worker from accessing their API on behalf of the user. That's what scope should be for. If scope was flipped around, having multiple Service Workers would be no problem as there would not be conflicts for the same scope. The use cases presented here are about external libraries and services that use their own Service Workers. Well, if SomeAnalyticsService needs to register, it can be scoped to its origin by default, or the developer can change the scope if necessary. Either way, it won't affect the main scope of the site. Scope is backwards as implemented today. |
Today, when a Web Performance service is interested in providing a Service Worker to help accelerate their customers’ sites, they have to make sure that their customers don’t already have a Service Worker that serves other use-cases on that same scope, or require their customers to modify their SW to include the acceleration service’s.
That may be tenable at the moment, but is likely to become less and less so, as more sites adopt Service Workers for flows that are more and more complex.
I’d love to see a way for such a service to install their own SW (which will be served from the customer’s domain), that would operate at a “lower layer” than the site’s SW, so that requests would hit the site’s SW first, then the service’s one, and responses would flow back up in the reverse order. The service’s SW would be closest to the network, as it may use proprietary protocols which require decoding before the response is sent to the site’s SW.
Libraries like ServiceWorkerWare seem to enable a similar flow, but they require all service workers to be written as middleware. Would be great to have a native way to do this, without requiring code changes to the site's SW.
What should the API enable?
I'm glad you asked ;)
Registration
Upon registration, SW will either opt-in to modular registration or not. If not, we can assume that order doesn’t matter for that SW and that it should override any other SW that didn’t opt-in to modular registration.
For SW that did opt-in to modular registration, they should be able to state if they want to run first (i.e. closest to the app), last (i.e. closest to the network) or (potentially?) somewhere in between.
Let's define "down the stack" as "closer to the network" for the explanations below.
Fetch flow
Fetch events should cascade between SWs where a
fetch()
call (or failure to callrespondWith()
) in one SW will trigger a fetch event in the one below it in the stack (or closer to the network). If there is none, the fetch should go to the network.A
respondWith()
call in a SW will return the promise of the fetch call issued by the nearest SW above it in the stack (or closer to the app). If there is none, the resource should return to Fetch (and the browser’s resource handlers).Events
For message, sync and push events that are registered from the page’s context, we need a way for the page to register them for a specific SW.
For sync and push events it seems simple as
SyncManager
/PushManager
are available onServiceWorkerRegistration
. Maybe a similar mechanism can be adopted for messages./cc @jakearchibald @crdumoul
The text was updated successfully, but these errors were encountered: