'use strict';
|
|
|
|
var EventEmitter = require('events').EventEmitter
|
|
, inherits = require('inherits')
|
|
, utils = require('../../utils/event')
|
|
, urlUtils = require('../../utils/url')
|
|
, XHR = global.XMLHttpRequest
|
|
;
|
|
|
|
var debug = function() {};
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
debug = require('debug')('sockjs-client:browser:xhr');
|
|
}
|
|
|
|
function AbstractXHRObject(method, url, payload, opts) {
|
|
debug(method, url);
|
|
var self = this;
|
|
EventEmitter.call(this);
|
|
|
|
setTimeout(function () {
|
|
self._start(method, url, payload, opts);
|
|
}, 0);
|
|
}
|
|
|
|
inherits(AbstractXHRObject, EventEmitter);
|
|
|
|
AbstractXHRObject.prototype._start = function(method, url, payload, opts) {
|
|
var self = this;
|
|
|
|
try {
|
|
this.xhr = new XHR();
|
|
} catch (x) {
|
|
// intentionally empty
|
|
}
|
|
|
|
if (!this.xhr) {
|
|
debug('no xhr');
|
|
this.emit('finish', 0, 'no xhr support');
|
|
this._cleanup();
|
|
return;
|
|
}
|
|
|
|
// several browsers cache POSTs
|
|
url = urlUtils.addQuery(url, 't=' + (+new Date()));
|
|
|
|
// Explorer tends to keep connection open, even after the
|
|
// tab gets closed: http://bugs.jquery.com/ticket/5280
|
|
this.unloadRef = utils.unloadAdd(function() {
|
|
debug('unload cleanup');
|
|
self._cleanup(true);
|
|
});
|
|
try {
|
|
this.xhr.open(method, url, true);
|
|
if (this.timeout && 'timeout' in this.xhr) {
|
|
this.xhr.timeout = this.timeout;
|
|
this.xhr.ontimeout = function() {
|
|
debug('xhr timeout');
|
|
self.emit('finish', 0, '');
|
|
self._cleanup(false);
|
|
};
|
|
}
|
|
} catch (e) {
|
|
debug('exception', e);
|
|
// IE raises an exception on wrong port.
|
|
this.emit('finish', 0, '');
|
|
this._cleanup(false);
|
|
return;
|
|
}
|
|
|
|
if ((!opts || !opts.noCredentials) && AbstractXHRObject.supportsCORS) {
|
|
debug('withCredentials');
|
|
// Mozilla docs says https://developer.mozilla.org/en/XMLHttpRequest :
|
|
// "This never affects same-site requests."
|
|
|
|
this.xhr.withCredentials = true;
|
|
}
|
|
if (opts && opts.headers) {
|
|
for (var key in opts.headers) {
|
|
this.xhr.setRequestHeader(key, opts.headers[key]);
|
|
}
|
|
}
|
|
|
|
this.xhr.onreadystatechange = function() {
|
|
if (self.xhr) {
|
|
var x = self.xhr;
|
|
var text, status;
|
|
debug('readyState', x.readyState);
|
|
switch (x.readyState) {
|
|
case 3:
|
|
// IE doesn't like peeking into responseText or status
|
|
// on Microsoft.XMLHTTP and readystate=3
|
|
try {
|
|
status = x.status;
|
|
text = x.responseText;
|
|
} catch (e) {
|
|
// intentionally empty
|
|
}
|
|
debug('status', status);
|
|
// IE returns 1223 for 204: http://bugs.jquery.com/ticket/1450
|
|
if (status === 1223) {
|
|
status = 204;
|
|
}
|
|
|
|
// IE does return readystate == 3 for 404 answers.
|
|
if (status === 200 && text && text.length > 0) {
|
|
debug('chunk');
|
|
self.emit('chunk', status, text);
|
|
}
|
|
break;
|
|
case 4:
|
|
status = x.status;
|
|
debug('status', status);
|
|
// IE returns 1223 for 204: http://bugs.jquery.com/ticket/1450
|
|
if (status === 1223) {
|
|
status = 204;
|
|
}
|
|
// IE returns this for a bad port
|
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa383770(v=vs.85).aspx
|
|
if (status === 12005 || status === 12029) {
|
|
status = 0;
|
|
}
|
|
|
|
debug('finish', status, x.responseText);
|
|
self.emit('finish', status, x.responseText);
|
|
self._cleanup(false);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
try {
|
|
self.xhr.send(payload);
|
|
} catch (e) {
|
|
self.emit('finish', 0, '');
|
|
self._cleanup(false);
|
|
}
|
|
};
|
|
|
|
AbstractXHRObject.prototype._cleanup = function(abort) {
|
|
debug('cleanup');
|
|
if (!this.xhr) {
|
|
return;
|
|
}
|
|
this.removeAllListeners();
|
|
utils.unloadDel(this.unloadRef);
|
|
|
|
// IE needs this field to be a function
|
|
this.xhr.onreadystatechange = function() {};
|
|
if (this.xhr.ontimeout) {
|
|
this.xhr.ontimeout = null;
|
|
}
|
|
|
|
if (abort) {
|
|
try {
|
|
this.xhr.abort();
|
|
} catch (x) {
|
|
// intentionally empty
|
|
}
|
|
}
|
|
this.unloadRef = this.xhr = null;
|
|
};
|
|
|
|
AbstractXHRObject.prototype.close = function() {
|
|
debug('close');
|
|
this._cleanup(true);
|
|
};
|
|
|
|
AbstractXHRObject.enabled = !!XHR;
|
|
// override XMLHttpRequest for IE6/7
|
|
// obfuscate to avoid firewalls
|
|
var axo = ['Active'].concat('Object').join('X');
|
|
if (!AbstractXHRObject.enabled && (axo in global)) {
|
|
debug('overriding xmlhttprequest');
|
|
XHR = function() {
|
|
try {
|
|
return new global[axo]('Microsoft.XMLHTTP');
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
};
|
|
AbstractXHRObject.enabled = !!new XHR();
|
|
}
|
|
|
|
var cors = false;
|
|
try {
|
|
cors = 'withCredentials' in new XHR();
|
|
} catch (ignored) {
|
|
// intentionally empty
|
|
}
|
|
|
|
AbstractXHRObject.supportsCORS = cors;
|
|
|
|
module.exports = AbstractXHRObject;
|