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