unfetch
unfetch copied to clipboard
XDomainRequest. IE 8 will not work when CORS requests
steps to reproduce
<!-- 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.
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);
};
@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.
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)
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 ? https://github.com/developit/unfetch/pull/107#issuecomment-453742671
else reject(Error());
I did not understand this thought
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.
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 ...