123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- '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;
|