arnaucube
arnaucube/comunicationLeapPublic Self-hosted repository
comunicationLeap/node_modules/engine.io-client/lib/transports/polling-xhr.js
node_modules/engine.io-client/lib/transports/polling-xhr.jsRaw
  1/**
  2 * Module requirements.
  3 */
  4
  5var XMLHttpRequest = require('xmlhttprequest');
  6var Polling = require('./polling');
  7var Emitter = require('component-emitter');
  8var inherit = require('component-inherit');
  9var debug = require('debug')('engine.io-client:polling-xhr');
 10
 11/**
 12 * Module exports.
 13 */
 14
 15module.exports = XHR;
 16module.exports.Request = Request;
 17
 18/**
 19 * Empty function
 20 */
 21
 22function empty(){}
 23
 24/**
 25 * XHR Polling constructor.
 26 *
 27 * @param {Object} opts
 28 * @api public
 29 */
 30
 31function XHR(opts){
 32  Polling.call(this, opts);
 33
 34  if (global.location) {
 35    var isSSL = 'https:' == location.protocol;
 36    var port = location.port;
 37
 38    // some user agents have empty `location.port`
 39    if (!port) {
 40      port = isSSL ? 443 : 80;
 41    }
 42
 43    this.xd = opts.hostname != global.location.hostname ||
 44      port != opts.port;
 45    this.xs = opts.secure != isSSL;
 46  }
 47}
 48
 49/**
 50 * Inherits from Polling.
 51 */
 52
 53inherit(XHR, Polling);
 54
 55/**
 56 * XHR supports binary
 57 */
 58
 59XHR.prototype.supportsBinary = true;
 60
 61/**
 62 * Creates a request.
 63 *
 64 * @param {String} method
 65 * @api private
 66 */
 67
 68XHR.prototype.request = function(opts){
 69  opts = opts || {};
 70  opts.uri = this.uri();
 71  opts.xd = this.xd;
 72  opts.xs = this.xs;
 73  opts.agent = this.agent || false;
 74  opts.supportsBinary = this.supportsBinary;
 75  opts.enablesXDR = this.enablesXDR;
 76  return new Request(opts);
 77};
 78
 79/**
 80 * Sends data.
 81 *
 82 * @param {String} data to send.
 83 * @param {Function} called upon flush.
 84 * @api private
 85 */
 86
 87XHR.prototype.doWrite = function(data, fn){
 88  var isBinary = typeof data !== 'string' && data !== undefined;
 89  var req = this.request({ method: 'POST', data: data, isBinary: isBinary });
 90  var self = this;
 91  req.on('success', fn);
 92  req.on('error', function(err){
 93    self.onError('xhr post error', err);
 94  });
 95  this.sendXhr = req;
 96};
 97
 98/**
 99 * Starts a poll cycle.
100 *
101 * @api private
102 */
103
104XHR.prototype.doPoll = function(){
105  debug('xhr poll');
106  var req = this.request();
107  var self = this;
108  req.on('data', function(data){
109    self.onData(data);
110  });
111  req.on('error', function(err){
112    self.onError('xhr poll error', err);
113  });
114  this.pollXhr = req;
115};
116
117/**
118 * Request constructor
119 *
120 * @param {Object} options
121 * @api public
122 */
123
124function Request(opts){
125  this.method = opts.method || 'GET';
126  this.uri = opts.uri;
127  this.xd = !!opts.xd;
128  this.xs = !!opts.xs;
129  this.async = false !== opts.async;
130  this.data = undefined != opts.data ? opts.data : null;
131  this.agent = opts.agent;
132  this.isBinary = opts.isBinary;
133  this.supportsBinary = opts.supportsBinary;
134  this.enablesXDR = opts.enablesXDR;
135  this.create();
136}
137
138/**
139 * Mix in `Emitter`.
140 */
141
142Emitter(Request.prototype);
143
144/**
145 * Creates the XHR object and sends the request.
146 *
147 * @api private
148 */
149
150Request.prototype.create = function(){
151  var xhr = this.xhr = new XMLHttpRequest({ agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR });
152  var self = this;
153
154  try {
155    debug('xhr open %s: %s', this.method, this.uri);
156    xhr.open(this.method, this.uri, this.async);
157    if (this.supportsBinary) {
158      // This has to be done after open because Firefox is stupid
159      // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension
160      xhr.responseType = 'arraybuffer';
161    }
162
163    if ('POST' == this.method) {
164      try {
165        if (this.isBinary) {
166          xhr.setRequestHeader('Content-type', 'application/octet-stream');
167        } else {
168          xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
169        }
170      } catch (e) {}
171    }
172
173    // ie6 check
174    if ('withCredentials' in xhr) {
175      xhr.withCredentials = true;
176    }
177
178    if (this.hasXDR()) {
179      xhr.onload = function(){
180        self.onLoad();
181      };
182      xhr.onerror = function(){
183        self.onError(xhr.responseText);
184      };
185    } else {
186      xhr.onreadystatechange = function(){
187        if (4 != xhr.readyState) return;
188        if (200 == xhr.status || 1223 == xhr.status) {
189          self.onLoad();
190        } else {
191          // make sure the `error` event handler that's user-set
192          // does not throw in the same tick and gets caught here
193          setTimeout(function(){
194            self.onError(xhr.status);
195          }, 0);
196        }
197      };
198    }
199
200    debug('xhr data %s', this.data);
201    xhr.send(this.data);
202  } catch (e) {
203    // Need to defer since .create() is called directly fhrom the constructor
204    // and thus the 'error' event can only be only bound *after* this exception
205    // occurs.  Therefore, also, we cannot throw here at all.
206    setTimeout(function() {
207      self.onError(e);
208    }, 0);
209    return;
210  }
211
212  if (global.document) {
213    this.index = Request.requestsCount++;
214    Request.requests[this.index] = this;
215  }
216};
217
218/**
219 * Called upon successful response.
220 *
221 * @api private
222 */
223
224Request.prototype.onSuccess = function(){
225  this.emit('success');
226  this.cleanup();
227};
228
229/**
230 * Called if we have data.
231 *
232 * @api private
233 */
234
235Request.prototype.onData = function(data){
236  this.emit('data', data);
237  this.onSuccess();
238};
239
240/**
241 * Called upon error.
242 *
243 * @api private
244 */
245
246Request.prototype.onError = function(err){
247  this.emit('error', err);
248  this.cleanup();
249};
250
251/**
252 * Cleans up house.
253 *
254 * @api private
255 */
256
257Request.prototype.cleanup = function(){
258  if ('undefined' == typeof this.xhr || null === this.xhr) {
259    return;
260  }
261  // xmlhttprequest
262  if (this.hasXDR()) {
263    this.xhr.onload = this.xhr.onerror = empty;
264  } else {
265    this.xhr.onreadystatechange = empty;
266  }
267
268  try {
269    this.xhr.abort();
270  } catch(e) {}
271
272  if (global.document) {
273    delete Request.requests[this.index];
274  }
275
276  this.xhr = null;
277};
278
279/**
280 * Called upon load.
281 *
282 * @api private
283 */
284
285Request.prototype.onLoad = function(){
286  var data;
287  try {
288    var contentType;
289    try {
290      contentType = this.xhr.getResponseHeader('Content-Type').split(';')[0];
291    } catch (e) {}
292    if (contentType === 'application/octet-stream') {
293      data = this.xhr.response;
294    } else {
295      if (!this.supportsBinary) {
296        data = this.xhr.responseText;
297      } else {
298        data = 'ok';
299      }
300    }
301  } catch (e) {
302    this.onError(e);
303  }
304  if (null != data) {
305    this.onData(data);
306  }
307};
308
309/**
310 * Check if it has XDomainRequest.
311 *
312 * @api private
313 */
314
315Request.prototype.hasXDR = function(){
316  return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR;
317};
318
319/**
320 * Aborts the request.
321 *
322 * @api public
323 */
324
325Request.prototype.abort = function(){
326  this.cleanup();
327};
328
329/**
330 * Aborts pending requests when unloading the window. This is needed to prevent
331 * memory leaks (e.g. when using IE) and to ensure that no spurious error is
332 * emitted.
333 */
334
335if (global.document) {
336  Request.requestsCount = 0;
337  Request.requests = {};
338  if (global.attachEvent) {
339    global.attachEvent('onunload', unloadHandler);
340  } else if (global.addEventListener) {
341    global.addEventListener('beforeunload', unloadHandler);
342  }
343}
344
345function unloadHandler() {
346  for (var i in Request.requests) {
347    if (Request.requests.hasOwnProperty(i)) {
348      Request.requests[i].abort();
349    }
350  }
351}