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

XDomainRequest. IE 8 will not work when CORS requests #106

Open
3F opened this issue Jan 8, 2019 · 6 comments
Open

XDomainRequest. IE 8 will not work when CORS requests #106

3F opened this issue Jan 8, 2019 · 6 comments

Comments

@3F
Copy link

3F commented Jan 8, 2019

steps to reproduce

[email protected]:

<!-- page1.html -->
<body>
  <script src="https://unpkg.com/[email protected]/polyfill/index.js"></script>
  <script src="https://unpkg.com/[email protected]/promise.min.js"></script>
  <script>
    fetch('page2.html')
        .then(function(response) {
            return response.text();
        })
        .then(function(data) {
            console.log(data);
        });
  </script>
    
<!-- page2.html -->
... any data ...

use any http server that supports Access-Control-Allow-Origin headers:

npm install http-server -g
http-server -p 8081 --cors
  • --cors - to enable CORS via the Access-Control-Allow-Origin

result

Through VM WinXP SP3 with IE version: 8.0.6001.18702 Update version: 0

  • F12 - Document Mode = 8 and 7.

and/or

  • Win10 IE11 - F12 - Emulation - Document Mode = 7 and 8 / User agent: 7/8

It does not work at all!

user-space solutions

Here's my example of hack for already released unfetch version like 4.0.1. Works fine for IE7+:

var send = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.send = function()
{
    this.onreadystatechange = function()
    {
        // TODO: prevent duplicate calling of onload if it's not CORS!
        if(this.readyState === 4 && this.status === 200) {
            return this.onload.apply(this, arguments);
        }
    }
    return send.apply(this, arguments);
}
 
// IE7+ ok now

Actually, even IE 11 with 5+ emulation, that of course strange :) because it should be ActiveXObject before IE7. Bad emulation :)

However, for real IE8 CORS support we should have requests through XDomainRequest not for XMLHttpRequest. Thus, your onload() is silent.

@developit
Copy link
Owner

Hi @3F - I'm not sure many libraries can be made to work just by aliasing XMLHttpRequest to XDomainRequest, since there are bugs in XDR that can cause requests to be queued indefinitely if not worked around. Instead, why not inject XDomainRequest support into XMLHttpRequest automatically?

var xhr2 = false;
try { xhr2 = 'useCredentials' in new XMLHttpRequest; } catch (e) {}
var old = window.XMLHttpRequest;
function XMLHttpRequest() {
  if (xhr2) return new old();
}
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
  var pageOrigin = location.protocol + ':' + location.host;
  var self = this;
  function wrap(m) {
    xhr[m] = function() {
      for (var i in xhr) if (typeof xhr[i] !== 'function') {
        try { self[i] = xhr[i]; } catch (e) {}
      }
      if (fn) fn();
      if (self[m]) self[m].apply(self, arguments);
    };
  }
  var xhr;
  if (url && /^https?:\/\//i.test(url) && url.indexOf(host)!==0 && self.XDomainRequest) {
    xhr = new XDomainRequest();
    wrap('onload', function() {
      self.readyState = 4;
      if (self.onreadystatechange) self.onreadystatechange();
    });
    wrap('onerror', function() {
      self.readyState = 4;
      if (self.onreadystatechange) self.onreadystatechange();
    });
  }
  else {
    xhr = new old();
    wrap('onreadystatechange', function() {
      if (xhr.readyState===4) {
        if (xhr.status && xhr.status <400 && xhr.status >= 200) {
          if (self.onload) self.onload();
        }
        else if (self.onerror) self.onerror(Error(0));
      }
    };
  }
  this._xhr = xhr;
  xhr.timeout = self.timeout;
  wrap('onprogress');
  wrap('ontimeout');
  xhr.open(method, url, async, user, pass); 
};
XMLHttpRequest.prototype.abort = function(){
  this._xhr.abort();
};
XMLHttpRequest.prototype.getAllResponseHeaders = function() {
  try { return this._xhr.getAllResponseHeaders(); } catch (e) {}
};
XMLHttpRequest.prototype.getResponseHeaders = function() {
  try { return this._xhr.getResponseHeaders(); } catch (e) {}
};
XMLHttpRequest.prototype.send = function() {
  setTimeout(function (x) { x.send(); }, 0, this._xhr); 
};

@3F
Copy link
Author

3F commented Jan 9, 2019

@developit
Why not to use XHR + onreadystatechange (#107) ? It seems works for IE and we have no major changes for other browsers. Because onload just wraps onreadystatechange with an readystate = 4, so basically it solves this issue even without XDomainRequest.

@developit
Copy link
Owner

developit commented Jan 14, 2019

I'd be willing to do something like this if it doesn't increase filesize:

xhr.onreadystatechange = () => {
  if (xhr.readyState==4) {
    if (xhr.status/100|0) == 2) resolve(response());
    else reject(Error());
  }
};

If we use onreadystatechange to avoid onload we should also be avoiding onerror. IIRC it isn't supported in IE8- either.

(Also FWIW IE's emulation is super inaccurate)

@3F
Copy link
Author

3F commented Jan 14, 2019

Also FWIW IE's emulation is super inaccurate

VM WinXP SP3 with the real IE version: 8.0.6001.18702 as I wrote above.

or what IE version do you mean? about other cool IE emulation I already noticed above.

if (xhr.status/100|0) == 2)

isn't it easier to use !== 0 ? or you've found some RFC ? #107 (comment)

else reject(Error());

I did not understand this thought

@developit
Copy link
Owner

Ah you're right - I was forgetting resolve() is called even if the status is not ok (outside [200,399]). Given that, your if (xhr.status) resolve(response()) would work.

My point about onerror was that onload and onerror are both unsupported in IE8. If we're moving to onreadystatechange to improve support in those older browsers, we should include the error event as well.

@3F
Copy link
Author

3F commented Jan 16, 2019

if (xhr.status)

hmm, yes, at least Firefox uses uint16_t and uint32_t

uint16_t XMLHttpRequestMainThread::ReadyState
uint32_t XMLHttpRequestMainThread::GetStatus

so OK, why not :)

My point about onerror was that onload and onerror are both unsupported in IE8.

Right, it was implemented as an additional events much more later.

:) However,

we should include the error event as well.

It means nothing to IE.

Well, looks like the mentioned IE 8 will not reset status to 0 even for the same failed requests. For example, via the same an abort operations:

xhr.onreadystatechange = function()
{
    if(this.readyState !== 4) return;
    console.log('evt: ', this.readyState, this.status);
    console.log('rcv len: ', this.responseText.length);
}

xhr.open('GET', 'http://192.168.1.10:8082/rcv', true); 
// remote data is equal to 125 829 120 bytes for this example
...

IE 8:

* when abort:

    evt: 4 200        <<<<<
    rcv len: 596719

    evt: 4 200        <<<<<
    rcv len: 8902359

    evt: 4 200        <<<<<
    rcv len: 2701359


* when no abort:

    evt: 4 200
    Exception: Not enough storage is available to complete this operation.
    ( hmmm o_o )

Firefox 64:

* when abort:

    evt: 4 0    <<<<<
    rcv len: 0


* when no abort:

    evt: 4 200 
    rcv len: 125829120

So what does this mean

We can finally use something like:

request.onreadystatechange = function() {
    if(this.readyState === 4) {
        this.status ? resolve(response()) : reject(Error());
        // ------------------------------------^ still not for IE
    }
};

But, we will never make IE happy as you can see above.

For other modern browsers will be raised onerror.

Or to be sure, for full coverage the old implementations, we can also invoke this reject. Unfortunately, I have no info about all this cases. So ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants