diff --git a/lib/transports/index.js b/lib/transports/index.js index c72a8edb..354f5754 100755 --- a/lib/transports/index.js +++ b/lib/transports/index.js @@ -24,6 +24,7 @@ exports.websocket = websocket; function polling(opts){ var xhr; var xd = false; + var xs = false; var jsonp = false !== opts.jsonp; if (global.location) { @@ -36,9 +37,11 @@ function polling(opts){ } xd = opts.hostname != location.hostname || port != opts.port; + xs = opts.secure != isSSL; } opts.xdomain = xd; + opts.xscheme = xs; xhr = new XMLHttpRequest(opts); if ('open' in xhr && !opts.forceJSONP) { diff --git a/lib/transports/polling-xhr.js b/lib/transports/polling-xhr.js index f7ea4323..82b647ec 100755 --- a/lib/transports/polling-xhr.js +++ b/lib/transports/polling-xhr.js @@ -42,6 +42,7 @@ function XHR(opts){ this.xd = opts.hostname != global.location.hostname || port != opts.port; + this.xs = opts.secure != isSSL; } } @@ -68,6 +69,7 @@ XHR.prototype.request = function(opts){ opts = opts || {}; opts.uri = this.uri(); opts.xd = this.xd; + opts.xs = this.xs; opts.agent = this.agent || false; opts.supportsBinary = this.supportsBinary; return new Request(opts); @@ -122,10 +124,13 @@ function Request(opts){ this.method = opts.method || 'GET'; this.uri = opts.uri; this.xd = !!opts.xd; + this.xs = !!opts.xs; this.async = false !== opts.async; this.data = undefined != opts.data ? opts.data : null; this.agent = opts.agent; - this.create(opts.isBinary, opts.supportsBinary); + this.isBinary = opts.isBinary; + this.supportsBinary = opts.supportsBinary; + this.create(); } /** @@ -140,14 +145,14 @@ Emitter(Request.prototype); * @api private */ -Request.prototype.create = function(isBinary, supportsBinary){ - var xhr = this.xhr = new XMLHttpRequest({ agent: this.agent, xdomain: this.xd }); +Request.prototype.create = function(){ + var xhr = this.xhr = new XMLHttpRequest({ agent: this.agent, xdomain: this.xd, xscheme: this.xs }); var self = this; try { debug('xhr open %s: %s', this.method, this.uri); xhr.open(this.method, this.uri, this.async); - if (supportsBinary) { + if (this.supportsBinary) { // This has to be done after open because Firefox is stupid // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension xhr.responseType = 'arraybuffer'; @@ -155,7 +160,7 @@ Request.prototype.create = function(isBinary, supportsBinary){ if ('POST' == this.method) { try { - if (isBinary) { + if (this.isBinary) { xhr.setRequestHeader('Content-type', 'application/octet-stream'); } else { xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); @@ -168,22 +173,18 @@ Request.prototype.create = function(isBinary, supportsBinary){ xhr.withCredentials = true; } - xhr.onreadystatechange = function(){ - var data; - - try { + if (this.hasXDR()) { + xhr.onload = function(){ + self.onLoad(); + }; + xhr.onerror = function(){ + self.onError(xhr.responseText); + }; + } else { + xhr.onreadystatechange = function(){ if (4 != xhr.readyState) return; if (200 == xhr.status || 1223 == xhr.status) { - var contentType = xhr.getResponseHeader('Content-Type'); - if (contentType === 'application/octet-stream') { - data = xhr.response; - } else { - if (!supportsBinary) { - data = xhr.responseText; - } else { - data = 'ok'; - } - } + self.onLoad(); } else { // make sure the `error` event handler that's user-set // does not throw in the same tick and gets caught here @@ -191,14 +192,8 @@ Request.prototype.create = function(isBinary, supportsBinary){ self.onError(xhr.status); }, 0); } - } catch (e) { - self.onError(e); - } - - if (null != data) { - self.onData(data); - } - }; + }; + } debug('xhr data %s', this.data); xhr.send(this.data); @@ -262,7 +257,11 @@ Request.prototype.cleanup = function(){ return; } // xmlhttprequest - this.xhr.onreadystatechange = empty; + if (this.hasXDR()) { + this.xhr.onload = this.xhr.onerror = empty; + } else { + this.xhr.onreadystatechange = empty; + } try { this.xhr.abort(); @@ -275,6 +274,46 @@ Request.prototype.cleanup = function(){ this.xhr = null; }; +/** + * Called upon load. + * + * @api private + */ + +Request.prototype.onLoad = function(){ + var data; + try { + var contentType; + try { + contentType = this.xhr.getResponseHeader('Content-Type'); + } catch (e) {} + if (contentType === 'application/octet-stream') { + data = this.xhr.response; + } else { + if (!this.supportsBinary) { + data = this.xhr.responseText; + } else { + data = 'ok'; + } + } + } catch (e) { + this.onError(e); + } + if (null != data) { + this.onData(data); + } +}; + +/** + * Check if it has XDomainRequest. + * + * @api private + */ + +Request.prototype.hasXDR = function(){ + return 'undefined' !== typeof global.XDomainRequest && !this.xs; +}; + /** * Aborts the request. * diff --git a/lib/xmlhttprequest.js b/lib/xmlhttprequest.js index ce3474ae..e2ddf2a4 100644 --- a/lib/xmlhttprequest.js +++ b/lib/xmlhttprequest.js @@ -3,6 +3,17 @@ var hasCORS = require('has-cors'); module.exports = function(opts) { var xdomain = opts.xdomain; + // scheme must be same when usign XDomainRequest + // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx + var xscheme = opts.xscheme; + + // Use XDomainRequest for IE8 because loading bar keeps flashing when using jsonp-polling + // https://github.com/yujiosaka/socke.io-ie8-loading-example + try { + if ('undefined' != typeof XDomainRequest && !xscheme) { + return new XDomainRequest(); + } + } catch (e) { } // XMLHttpRequest can be disabled on IE try { diff --git a/test/index.js b/test/index.js index 2cb00be1..0b18a2d5 100644 --- a/test/index.js +++ b/test/index.js @@ -17,6 +17,7 @@ require('./transport'); // browser only tests if (env.browser) { require('./connection'); + require('./xmlhttprequest'); if (global.ArrayBuffer) { require('./arraybuffer'); } else { diff --git a/test/xmlhttprequest.js b/test/xmlhttprequest.js new file mode 100644 index 00000000..8425f64b --- /dev/null +++ b/test/xmlhttprequest.js @@ -0,0 +1,24 @@ +var expect = require('expect.js'); +var XMLHttpRequest = require('../lib/xmlhttprequest'); +var isIE8 = /MSIE 8/.test(navigator.userAgent); + +describe('XMLHttpRequest', function () { + + if (isIE8) { + describe('IE8', function() { + it('should have same properties as XDomainRequest does when xscheme is false', function() { + var xhr = new XMLHttpRequest({xdomain: false, xscheme: false}); + expect(xhr).to.be.an('object'); + expect(xhr).to.have.property('onload'); + expect(xhr).to.have.property('onerror'); + }); + + it('should have same properties as XMLHttpRequest does when xscheme is true', function() { + var xhr = new XMLHttpRequest({xdomain: false, xscheme: true}); + expect(xhr).to.be.an('object'); + expect(xhr).to.have.property('onreadystatechange'); + }); + }); + } + +});