EventSource icon indicating copy to clipboard operation
EventSource copied to clipboard

.close() does not abort current event source fetch connections

Open svyotov opened this issue 6 years ago • 20 comments

Fetch supports abort, if it is not provided or called it leaves long leaving TCP connections open. Abort package : https://www.npmjs.com/package/abortcontroller-polyfill

Example code/changes that could make this work:

  1. Create a controller as described in https://www.npmjs.com/package/abortcontroller-polyfill

  2. Add the abort signal to the call of transport

  transport.open(xhr, onStart, onProgress, onFinish, requestURL, withCredentials, requestHeaders, controller.signal);
  1. Store the controller in order to be able to access it in close
    es.controller = controller; 
  1. Update close
  EventSourcePolyfill.prototype.close = function () {
    this.controller.abort();
    this._close();
  };
  1. Update the definition of transport when the browser supports fetch
  FetchTransport.prototype.open = function (xhr, onStartCallback, onProgressCallback, onFinishCallback, url, withCredentials, headers, signal) {
    // cache: "no-store"
    // https://bugs.chromium.org/p/chromium/issues/detail?id=453190
    var textDecoder = new TextDecoder();
    fetch(url, {
      headers: headers,
      credentials: withCredentials ? "include" : "same-origin",
      signal: signal
    })

svyotov avatar Oct 23 '18 13:10 svyotov

Why? reader.cancel() is used to cancel the request. Doesn't it abort the connection? Of course, it will not work unless the connection gets the "reading state", but it is not so bad, seems.

Abort package : https://www.npmjs.com/package/abortcontroller-polyfill "This "polyfill" doesn't actually close the connection when the request is aborted,"

Add the abort signal to the call of transport In fact, on line 439 - https://github.com/Yaffle/EventSource/blob/master/src/eventsource.js#L439 - the onStartCallback is called with a function that should stop the fetch request using reader.stop().

Thanks

Yaffle avatar Oct 23 '18 17:10 Yaffle

We did some experiments, and the connections never got closed when we were calling the .close(). It also does not seem to work when we tested with multiple event streams.

    fetch(url, {
      headers: headers,
      credentials: withCredentials
        ? "include"
        : "same-origin"
    }).then(function(response) {

to my best understanding will wait for the fetch to complete and get a response before it gets cancelled, while the other approach will close it immediately. If we are not seeing something and it is already implemented, could you point us to a documentation or provide a small sample on how to close the event source when the component which created the event source gets destroyed?

svyotov avatar Oct 24 '18 11:10 svyotov

It seems, the reader.cancel() does not close the connection in Edge.

Yaffle avatar Oct 24 '18 17:10 Yaffle

AbortController also doesn't help.

Yaffle avatar Oct 24 '18 18:10 Yaffle

try to use the Transport option:

var es = new EventSource(url, {
  Transport: XMLHttpRequest
});

I see no solutions with Fetch API.

Yaffle avatar Oct 24 '18 18:10 Yaffle

this:

  fetch(url, {
      headers: headers,
      credentials: withCredentials ? "include" : "same-origin",
      signal: signal
    })

worked for me.

svyotov avatar Oct 25 '18 16:10 svyotov

https://github.com/Yaffle/EventSource/commit/fd5408ef717b4679c186c80644dd46226176f03d - I have tried to use the suggested changes.

Yaffle avatar Oct 25 '18 16:10 Yaffle

@Yaffle Thank you!

  EventSourcePolyfill.prototype.close = function() {
    this.controller.abort();
    this._close();
  };

I personally added the abort to the es.close(), so the user can call it at any time. However I do not understand the code enough to say if that is necessary. Will try to test your version tomorrow.

svyotov avatar Oct 25 '18 20:10 svyotov

@svyotov , does it work? I have no Edge 18, but it does it work in Edge 17.

Yaffle avatar Nov 02 '18 06:11 Yaffle

@Yaffle we decided to go with a different approach for other reasons, so unfortunately I will not be able to test it. Feel free to close the issue, and thank you for the help!

svyotov avatar Nov 07 '18 16:11 svyotov

I have tested Edge 18 - it is still doesn't close connection when calling EventSource#close with any AbortController or Reader#cancel

Yaffle avatar Dec 07 '18 06:12 Yaffle

@Yaffle Hi, I found out that before the server sends the 1st message, the initialization of EventSourcePolyfill is not fully done (because onStart never called) so the es.close() doesn't abort the connection.

Also if the server sends the first message instantly it will be only handled by the client together with the second message.

I've done all tests with the latest Chrome(71), using EventSourcePolyfill only

requilence avatar Feb 11 '19 10:02 requilence

@requilence, hi Yes, this looks like a bug. I think, the solution here is to call cancelFunction in case the state is CLOSED. Or to add the AbortController. I would prefer the first, and send somethink from the server:-)

Yaffle avatar Feb 11 '19 11:02 Yaffle

Thanks for the fast answer! You don't always have the ability to change the server's code :) Even if you have, there is always a possibility that user, for example, opens the route and then switch it(i mean with no page reload) before server answers on EventSource.

So it will be better to abort the connection with no response from the server. Maybe return AbortController from the transport.open or add the onOpen arg?

requilence avatar Feb 12 '19 08:02 requilence

I did not want to use the AbortController as this is an extra entity and it is not supported on some browsers. Although, I will accept it.

Yaffle avatar Feb 12 '19 09:02 Yaffle

I think, the open method may return a function which aborts the connection. And the AbortController polyfill will need to be updated (it will be needed to replace native fetch with a wrapper) to call reader cancel()

Yaffle avatar Feb 12 '19 09:02 Yaffle

@requilence it is implemented now

Yaffle avatar Jun 27 '19 22:06 Yaffle

Thanks a lot! I will be able to test it next week

requilence avatar Jun 27 '19 22:06 requilence

any updates on this?

matiishyn avatar Aug 22 '19 14:08 matiishyn

Hi, seems like it doesn't work. Tested on Chrome, latest version. Any workround ?

gal-terra avatar Oct 30 '19 20:10 gal-terra