jpillora / xdomain

A pure JavaScript CORS alternative
https://jpillora.com/xdomain/
3.12k stars 270 forks source link

Bug: Close XHR on iframe timeout #102

Closed yoorek closed 9 years ago

yoorek commented 10 years ago

I am writing AngularJS application. Everything works fine and erroes are handled correctly except one case - when proxy.html required by xdomain is not accesible (network or server error)

In that case I get:

GET https://server.test.pl:41012/proxy.html net::ERR_NAME_NOT_RESOLVED
  getFrame  xdomain.js:520
  (anonymous function)  xdomain.js:616
  facade.send.process   xdomain.js:446
  facade.send   xdomain.js:449
  (anonymous function)  angular.js:8521
  sendReq   angular.js:8315
  $HttpProvider.$get.serverRequest  angular.js:8049
  deferred.promise.then.wrappedCallback angular.js:11520
  (anonymous function)  angular.js:11606
  $RootScopeProvider.$get.Scope.$eval   angular.js:12632
  $RootScopeProvider.$get.Scope.$digest angular.js:12444
  $RootScopeProvider.$get.Scope.$apply  angular.js:12736
  (anonymous function)  angular.js:1438
  invoke    angular.js:3940
  doBootstrap   angular.js:1436
  bootstrap angular.js:1450
  (anonymous function)  app.js:86
  trigger   angular.js:2563
  (anonymous function)  angular.js:2843
  forEach   angular.js:325
  eventHandler  angular.js:2842

I want to intercept this error and handle it but problem is I don't know how because for this error:

How can I handle this?

jpillora commented 10 years ago

Your proxy URL is wrong?

On Wednesday, July 30, 2014, yoorek notifications@github.com wrote:

I am writing AngularJS application. Everything works fine and erroes are handled correctly except one case - when proxy.html required by xdomain is not accesible (network or server error)

In that case I get:

GET https://server.test.pl:41012/proxy.html net::ERR_NAME_NOT_RESOLVED getFrame xdomain.js:520 (anonymous function) xdomain.js:616 facade.send.process xdomain.js:446 facade.send xdomain.js:449 (anonymous function) angular.js:8521 sendReq angular.js:8315 $HttpProvider.$get.serverRequest angular.js:8049 deferred.promise.then.wrappedCallback angular.js:11520 (anonymous function) angular.js:11606 $RootScopeProvider.$get.Scope.$eval angular.js:12632 $RootScopeProvider.$get.Scope.$digest angular.js:12444 $RootScopeProvider.$get.Scope.$apply angular.js:12736 (anonymous function) angular.js:1438 invoke angular.js:3940 doBootstrap angular.js:1436 bootstrap angular.js:1450 (anonymous function) app.js:86 trigger angular.js:2563 (anonymous function) angular.js:2843 forEach angular.js:325 eventHandler angular.js:2842

I want to intercept this error and handle it but problem is I don't know how because for this error:

  • $http catch/error promise method is not called
  • httInterceptor i wrote is not invoked

How can I handle this?

— Reply to this email directly or view it on GitHub https://github.com/jpillora/xdomain/issues/102.

jpillora commented 10 years ago

ERR_NAME_NOT_RESOLVED is a DNS issue, your stack trace is failing when XDomain is adding the proxy iframe to the DOM. Check your proxy URL and report back please

yoorek commented 10 years ago

I know it is DNS issue because network is off (for testing) - the thing is how can I handle this kind of error - when xdomain fails during adding iframe. From application point of view it’s just „failed http request” and needs to be handled properly - server is down, network is down. When using pure Angular $http service I have no problem with handling network problems because catch/error clause is called during request. When I use xdomain this kind of errors are „swallowed” by xdomain.

Wiadomość napisana przez Jaime Pillora notifications@github.com w dniu 30 lip 2014, o godz. 08:44:

ERR_NAME_NOT_RESOLVED is a DNS issue, your stack trace is failing when XDomain is adding the proxy iframe to the DOM. Check your proxy URL and report back please

— Reply to this email directly or view it on GitHub.

jpillora commented 10 years ago

$http uses XMLHttpRequest. iframes do not. You cannot catch iframe errors.

With:

var frame = document.createElement("iframe");
frame.id = frame.name = "foo";
frame.src = "http://dns-failure-test-domain-abc-xyz.com";
frame.setAttribute('style', 'display:none;');
frame.onload = function(event) { console.info('load', event); };
frame.onerror = function(err) { console.info('error', err); };
document.body.appendChild(frame);

Running: http://jsfiddle.net/jpillora/2prH2/

In the console we see:

GET http://dns-failure-test-domain-abc-xyz.com/ net::ERR_NAME_NOT_RESOLVED (index):28
load Event {clipboardData: undefined, path: NodeList[4], cancelBubble: false, returnValue: true, srcElement: iframe#foo…}

The GET error above is the same as yours and my onerror handler did not fire. Currently XDomain will emit an iframe timeout which is the best it can do.

If you want nice offline UX, I suggest using https://developer.mozilla.org/en/docs/Online_and_offline_events and do not even attempt XHR while offline.

yoorek commented 10 years ago

online / offline events do not solve the proxy.html server down problem - is there any solution to this?

if only timeout - how can i handle xdomain 'Timeout waiting on iframe socket' ? It seems like it's just a warning message

I still don't understand why you cannot abort XHR with error 'timeout' when you detect timeout - this way angular could have a chance to properly call error promise.

Also - when i add timeout to Angular $http service config it works (request is aborted) but with xdomain ends only with console message : "send socket: xdomain-243fa40f: abort" - it is not propagated back to Angular:

  if (window.xdomain) {
    var slave = {};
    slave['https://bubi.one2tribe.pl:41012'] = '/proxy.html';

    window.xdomain.slaves(slave);
    window.xdomain.debug = true;
  }

  var $injector = angular.injector(['ng']);
  var $http = $injector.get('$http');

  $http({
    method: 'GET',
    url: 'https://bubi.one2tribe.pl:41012/proxy.html',
    timeout: 1000
  })
      .success(function () {
        console.log('SUCCESS');
      })
      .error(function () {
        console.log('ERROR');
      })
      .finally(function () {
        console.log('FINALLY');
      });

No matter what - nothing is called when proxy.html is not available - even with timeout.

So, I really cannot see any solution to "server is not available' which is a killer :-(

Wiadomość napisana przez Jaime Pillora notifications@github.com w dniu 30 lip 2014, o godz. 09:14:

$http uses XMLHttpRequest. iframes do not. You cannot catch iframe errors.

With:

var frame = document.createElement("iframe"); frame.id = frame.name = "foo"; frame.src = "http://dns-failure-test-domain-abc-xyz.com"; frame.setAttribute('style', 'display:none;'); frame.onload = function(event) { console.info('load', event); }; frame.onerror = function(err) { console.info('error', err); }; document.body.appendChild(frame); Running: http://jsfiddle.net/jpillora/2prH2/

In the console we see:

GET http://dns-failure-test-domain-abc-xyz.com/ net::ERR_NAME_NOT_RESOLVED (index):28 load Event {clipboardData: undefined, path: NodeList[4], cancelBubble: false, returnValue: true, srcElement: iframe#foo…} The GET error above is the same as yours and my onerror handler did not fire. Currently XDomain will emit an iframe timeout which is the best it can do.

If you want nice offline UX, I suggest using https://developer.mozilla.org/en/docs/Online_and_offline_events and do not even attempt XHR while offline.

— Reply to this email directly or view it on GitHub.

vinnu-313 commented 9 years ago

Even I faced the same issue and ended up using jquery for one call that checks the server for availability and jquery invoke the error function as expected.

$.ajax({ url : http://infoserver.glassbeam.com/xproxy, timeout : 15000, success : function(response) { console.log("Success"); }, error : function(response) { console.log("Error"); });

Still jquery solution works, I wanna move away from jquery and use only angularJS. So it'll be useful to have it working in angularJS as well.

jpillora commented 9 years ago

Marked as a bug, will look into it when I can

fejalish commented 9 years ago

I had a similar issue with an Ember app. Killing the internet connection after the app had loaded would cause any subsequent AJAX requests to appear to be continually loading even after a timeout. As a workaround we set a global timeout for jQuery AJAX calls and that fixed our problems.

$.ajaxSetup({ timeout: 15000 });

It would be great to get an error back into the app from xdomain, but this worked quite well regardless. :-)

iameli commented 9 years ago

Probably this should be expanded to something like "close XHR on any sort of xdomain socket error", yeah? I was getting various postMessage exceptions on Firefox wrt https://github.com/jpillora/xdomain/issues/119, and would have loved for my angular $http error handlers to be called so I could fall back to a different method, but everything just sort of hangs after an exception or an iframe timeout.

jpillora commented 9 years ago

Correct, all socket errors should close the underlying xhr

On Saturday, October 11, 2014, Eli Mallon notifications@github.com wrote:

Probably this should be expanded to something like "close XHR on any sort of xdomain socket error", yeah? I was getting various postMessage exceptions on Firefox wrt #119 https://github.com/jpillora/xdomain/issues/119, and would have loved for my angular $http error handlers to be called so I could fall back to a different method, but everything just sort of hangs after an exception or an iframe timeout.

— Reply to this email directly or view it on GitHub https://github.com/jpillora/xdomain/issues/102#issuecomment-58725808.

jpillora commented 9 years ago

I just vaguely remembered that I previously attempted to check for postmessage failures, though I found iframes don't emit error events, and I don't think I could try catch the error either... Will have to re-test...

jpillora commented 9 years ago

This should be fixed, let me know if it still isnt working for you