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

feat(service-worker): Support ignoring specific URLs (e.g. for AJAX progress listener, video streaming, range requests, Firebase uploads) #21191

Closed
MrCroft opened this issue Dec 27, 2017 · 46 comments
Labels
area: service-worker Issues related to the @angular/service-worker package feature Issue that requests a new feature
Milestone

Comments

@MrCroft
Copy link

MrCroft commented Dec 27, 2017

I'm submitting a...

[x] Feature request

Current behavior

ServiceWorker breaks AJAX uploads progress reporting.

Expected behavior

AJAX upload progress listener should work.

What is the motivation / use case for changing the behavior?

Progress is needed when handling uploads.
There is an open issue here and it is on this w3c roadmap
Until then, as far as I could read, it would be possible to make this work by checking the event.request.url at the very top of the SW script and simply return if it matches certain urls that would be specified in ngsw-config.json

Environment

Angular version: 5.1.2
@alxhub alxhub added area: service-worker Issues related to the @angular/service-worker package feature Issue that requests a new feature labels Jan 2, 2018
@doronsever
Copy link

We have the same problem either.
For now, we added an ugly spinner instead of a progress bar but our clients aren't happy with it.

It would be best if this topic can be priorities somehow

@ngbot ngbot bot added this to the Backlog milestone Jan 23, 2018
@MrCroft
Copy link
Author

MrCroft commented Jan 24, 2018

@doronsever
In case this is critical for you, I've just tested with editing the resulted ngsw-worker.js file and it works:
I've installed gulp and gulp-replace 😬 and created a gulp task that replaces onFetch(event) { with:

onFetch(event) {
    if (event.request.url.indexOf('/upload') !== -1) { return; }

in the dist/ngsw-worker.js file and then my build script became ng build [whatever options I have] && gulp swAjaxUploadFix (or I could have used the default task and simply run gulp, but I like to name my tasks).
Of course, in my case we have the convention that all upload urls contain /upload.
And also I log a big, multiple line message in the console on build so I don't forget about removing this workaround as soon as this feature gets implemented by the Angular team.
But, meanwhile, reporting the progress on uploads works! 😁
So, I just wanted to let you know it works. Because when I posted the issue, I had only read that this would fix progress reporting, and took what I read for granted, haven't tested myself. Now I have!

@doronsever
Copy link

@MrCroft Thank you very much!
At least it's working now :)

@ganeshkbhat
Copy link

Can this actually be put as a feature request from my end also? I think something having a key like "exclude" with interface like interface Exclude { name: string; urls: string[]; version?: number; }. There is one more open question like this in 'so. https://stackoverflow.com/questions/47693093/angular-5-and-service-worker-how-to-exclude-a-particular-path-from-ngsw-config

@gkalpak
Copy link
Member

gkalpak commented May 5, 2018

The only problem I see is that the check should happen asynchronously. Based on #23025 (comment) it shouldn't be possible to decide whether to handle the request or not asycnhronously. But I am wondering if calling event.waitUntil() allows us to defer the decision of whether to call event.respondWith() or not and still be able to choose not to handle the request.

I.e. something like:

onFetch(evt) {
  evt.waitUntil(asynchronouslyDecideWhetherToHandle(evt).
    then(handleIt => handleIt && evt.respondWith(...));
}

@ganeshkbhat
Copy link

ganeshkbhat commented May 18, 2018

I see the point. If thats the case it definitely seems like its a tradeoff on performance and feature. May be someone is is more aware than me on the implementation can point the way forward.

@gkalpak gkalpak changed the title [ServiceWorker] Possibility to have urls that bypass the ServiceWorker (otherwise, Ajax progress listener doesn't work) feat(service-worker): Support ignoring specific URLs (e.g. for AJAX progress listener, video streaming, range requests) Sep 17, 2018
@gkalpak
Copy link
Member

gkalpak commented Sep 17, 2018

This would also be useful as a workaround for Firebase uploads (#23244), video streaming (#25007) and range requests (e.g. seeking in videos) (#25865).
A possible solution could come through #20756 (comment).

@gkalpak gkalpak changed the title feat(service-worker): Support ignoring specific URLs (e.g. for AJAX progress listener, video streaming, range requests) feat(service-worker): Support ignoring specific URLs (e.g. for AJAX progress listener, video streaming, range requests, Firebase uploads) Sep 17, 2018
@GXGOW
Copy link

GXGOW commented Dec 19, 2018

I'm having the same issue when watching server sent events through an EventStream. Bypassing the 'notifications' request with aforementioned workaround fixed it for now though.

@Delagen
Copy link
Contributor

Delagen commented Dec 20, 2018

This issue is blocking rendering PDF in Chrome from 71 version

@aSegatto
Copy link

aSegatto commented Dec 21, 2018

We are also having problems with PDF documents, they are correctly served by the Service worker, but they don't get rendered. Bypassing the sw with the console allows to see the PDF document.

@webmaxru
Copy link
Member

webmaxru commented Jan 1, 2019

Did you try specifying navigationUrlsexplicitly?
https://angular.io/guide/service-worker-config#navigationurls

@aSegatto
Copy link

aSegatto commented Jan 6, 2019

Did you try specifying navigationUrlsexplicitly?
https://angular.io/guide/service-worker-config#navigationurls

what do you mean ? all files with extension are excluded with a glob; or am I missing something ?

@joaqcid
Copy link

joaqcid commented Jan 18, 2019

as @gkalpak says, the service worker doesnt work on safari when uploading files with @angular/fire to firebase storage

@avilao
Copy link

avilao commented Feb 12, 2019

Any roadmap on this issue?

@Iyashu5040
Copy link

Plus one for this. We're hitting issues with the service worker adding "Cache-Control" headers to external API calls which don't allow that header. As we're not in control of the API we can't modify the accept headers on it. Therefore we need some way to have the browser do these API calls normally.

@gkalpak
Copy link
Member

gkalpak commented Apr 18, 2019

@lyashu5040, it won't be able to address those cases (but it will address cases all other cases).
It is not an ideal solution, but it is the best we've come up with so far 😁

@gkalpak
Copy link
Member

gkalpak commented Apr 18, 2019

BTW, for anyone desperate for a work-around, I've seen people successfully wrapping ngsw-worker.js in their own script that catches fetch events early and stops their propagation to ngsw-worker.js. Off the top of my head, something like:

// Catch `fetch` events and prevent specific ones from propagating to the Angular SW.
self.addEventListener('fetch', evt => {
  const req = evt.request;

  if (/* `req` matches some criteria */) {
    evt.stopImmediatePropagation();
  }
});

// Import the Angular SW to do the heavy-lifting.
self.importScripts('./ngsw-worker.js');

If you do that, you might want to set updateViaCache: 'all', when registering the SW.

I am not sure this will work (and it is definitely not an officially recommended approach 😇), but might be good enough as a work-around.

petersalomonsen added a commit to petersalomonsen/angular that referenced this issue Apr 21, 2019
issue angular#21191

added ngsw-bypass header and queryparameter
for bypassing serviceworker on specific requests
@petersalomonsen
Copy link
Contributor

Started work on a pull request: #30010 @gkalpak

petersalomonsen added a commit to petersalomonsen/angular that referenced this issue Apr 21, 2019
issue angular#21191

added ngsw-bypass header and queryparameter
for bypassing serviceworker on specific requests
petersalomonsen added a commit to petersalomonsen/angular that referenced this issue Apr 21, 2019
issue angular#21191

added ngsw-bypass header and queryparameter
for bypassing serviceworker on specific requests
petersalomonsen added a commit to petersalomonsen/angular that referenced this issue Apr 23, 2019
… param

Add support for bypassing the ServiceWorker for a request by using the ngsw-bypass header or query
parameter.

Fixes angular#21191
petersalomonsen added a commit to petersalomonsen/angular that referenced this issue Apr 24, 2019
… param

Add support for bypassing the ServiceWorker for a request by using the
ngsw-bypass header or query parameter.

Fixes angular#21191
@frankadrian
Copy link

frankadrian commented Apr 24, 2019

I found a fairly simple workaround if you want your SW to completely "ignore" a specific path (for example we had a few paths that get redirected to another domain, which we did not want to be handled by our SW) :
By adding paths, you want to ignore, with a cacheConfig.strategy of "freshness" as a dataGroup the service worker will automatically load the content from the server (given that the timeout is not reached.)

eg ngsw-config.json:

...
"dataGroups": [
{
  "name": "cloudfront behaviours",
  "urls": [
    // for these paths the Service Worker is ignored
    "/terms",
    "/blog-posts"
  ],
  "cacheConfig": {
    "maxSize": 0,
    "maxAge": "0u",
    "strategy": "freshness"
  }
},
... 

@gkalpak
Copy link
Member

gkalpak commented Apr 24, 2019

@frankadrian, this is something different. In this case, the SW will still handle the request, but it will just choose to forward it to the server and forward the response back to the app. This is already possible without specifying a data-group (i.e. is the difault behavior).

This thread is about having the SW not handle the request (and let the browser handle it instead). There is a subtle difference, but it can be important in certain cases (for reasons explained above).

petersalomonsen added a commit to petersalomonsen/angular that referenced this issue Apr 24, 2019
… param

Add support for bypassing the ServiceWorker for a request by using the
ngsw-bypass header or query parameter.

Fixes angular#21191
petersalomonsen added a commit to petersalomonsen/angular that referenced this issue Apr 24, 2019
… param

Add support for bypassing the ServiceWorker for a request by using the
ngsw-bypass header or query parameter.

Fixes angular#21191
petersalomonsen added a commit to petersalomonsen/angular that referenced this issue Apr 24, 2019
… param

Add support for bypassing the ServiceWorker for a request by using the
ngsw-bypass header or query parameter.

Fixes angular#21191
gkalpak pushed a commit to petersalomonsen/angular that referenced this issue Apr 25, 2019
… param

Add support for bypassing the ServiceWorker for a request by using the
ngsw-bypass header or query parameter.

Fixes angular#21191
@aSegatto
Copy link

Please don't close this. As was stressed before the current implementation doesn't fix all usecases, eg. when using firebase SDK.

@petersalomonsen
Copy link
Contributor

petersalomonsen commented Apr 26, 2019

@aSegatto Have you tried using an HttpInterceptor: https://angular.io/api/common/http/HttpInterceptor

It could maybe capture the requests made by the firebase SDK? And in that case add the ngsw-bypass header.

Guide for using HttpInterceptor can be found here: https://angular.io/guide/http#intercepting-requests-and-responses

@gkalpak
Copy link
Member

gkalpak commented Apr 27, 2019

@petersalomonsen, I don't think the Firebase SDK goes through the HttpClient (for all requests), but I might be wrong.

@aSegatto, fair enough. I suggest we open a new issue specifically for situations where using an HTTP header or query param is not possible (since that is slightly different than the original issue). Can you do that (and reference this issue for context)? (Also, I think it is worth mentioning this work-around for visibility: #21191 (comment))

@petersalomonsen
Copy link
Contributor

petersalomonsen commented Apr 28, 2019

@aSegatto @gkalpak I've had a quick look at AngularFire and seems like it's not using HttpClient. But even when this is the case we can still intercept XMLHttpRequest for example like this:

const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function( method, url) { 
	originalOpen.apply(this,[method, url]);
	this.setRequestHeader('ngsw-bybass','true');
};

Then any library using XMLHttpRequest in your client will set the ngsw-bypass header. You can extend this to check for specific URLs / methods.

We could of course add another feature to the serviceworker by using the message port to send lists of URLs to be bypassed (and the serviceworker could store this in a map), but I think I would try out the client side interception strategy first.

@aSegatto
Copy link

@petersalomonsen Thanks for your suggestions. I don't know if modifying XMLHttpRequest would work , because i don't know how the requests are made from the firebase sdk (maybe they also use fetch API ? ), but this could be an approach...
Message port IMHO is more clean as it doesn't couple to the sdk implementation.

@gkalpak Thanks, I open another issue

bibyzan pushed a commit to bibyzan/stitch-js-sdk that referenced this issue May 2, 2019
…e worker onFetch implementation and lets the browser handle the request, specific to angular.

Background on root issue w3c/ServiceWorker#885
Issue leading to work around angular/angular#21191
Workaround that checks for the header petersalomonsen/angular@c7b357a
BioPhoton pushed a commit to BioPhoton/angular that referenced this issue May 21, 2019
… param (angular#30010)

Add support for bypassing the ServiceWorker for a request by using the
ngsw-bypass header or query parameter.

Fixes angular#21191

PR Close angular#30010
@briznad
Copy link

briznad commented Jun 7, 2019

To expand upon @MrCroft 's workaround, in case anyone isn't using gulp/grunt or other build tool, you can modify the ngsw-worker.js file using sed and easily integrate the workaround into your package.json scripts. Here's the code I'm using on MacOS 10.14.5:

in package.json

"scripts": {
    …
    "editngsw": "sed -i '' \"s/onFetch(event) {/onFetch(event) { if (event.request.url.indexOf('firebasestorage.googleapis.com') !== -1) { return; }/g\" www/ngsw-worker.js",
    "build": "ng build --prod=true && npm run editngsw"
  },

or for Ionic

"scripts": {
    …
    "editngsw": "sed -i '' \"s/onFetch(event) {/onFetch(event) { if (event.request.url.indexOf('firebasestorage.googleapis.com') !== -1) { return; }/g\" www/ngsw-worker.js",
    "build": "ionic build --prod && npm run editngsw"
  },

Please note: the above code assumes you're using www as your build output directory (vs dist, etc).

@BluebambooSRL
Copy link

BluebambooSRL commented Jul 22, 2019

@briznad @MrCroft many thanks for the solution! It also works for me :)
There is any way to write the code at the @angular/pwa library in order to avoid tho search for this bug? @gkalpak @AngularFirebase @fireship-io Thanks in advance!! Federico

@gkalpak
Copy link
Member

gkalpak commented Jul 22, 2019

@BluebambooSRL: Sorry, I disn't understand your question 😕 Can you clarify?

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 15, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: service-worker Issues related to the @angular/service-worker package feature Issue that requests a new feature
Projects
None yet
Development

No branches or pull requests