diff options
Diffstat (limited to 'node_modules/engine.io-client/lib/transports/polling-xhr.js')
| -rw-r--r-- | node_modules/engine.io-client/lib/transports/polling-xhr.js | 413 | 
1 files changed, 413 insertions, 0 deletions
| diff --git a/node_modules/engine.io-client/lib/transports/polling-xhr.js b/node_modules/engine.io-client/lib/transports/polling-xhr.js new file mode 100644 index 0000000..297bac5 --- /dev/null +++ b/node_modules/engine.io-client/lib/transports/polling-xhr.js @@ -0,0 +1,413 @@ +/** + * Module requirements. + */ + +var XMLHttpRequest = require('xmlhttprequest-ssl'); +var Polling = require('./polling'); +var Emitter = require('component-emitter'); +var inherit = require('component-inherit'); +var debug = require('debug')('engine.io-client:polling-xhr'); + +/** + * Module exports. + */ + +module.exports = XHR; +module.exports.Request = Request; + +/** + * Empty function + */ + +function empty () {} + +/** + * XHR Polling constructor. + * + * @param {Object} opts + * @api public + */ + +function XHR (opts) { +  Polling.call(this, opts); +  this.requestTimeout = opts.requestTimeout; +  this.extraHeaders = opts.extraHeaders; + +  if (global.location) { +    var isSSL = 'https:' === location.protocol; +    var port = location.port; + +    // some user agents have empty `location.port` +    if (!port) { +      port = isSSL ? 443 : 80; +    } + +    this.xd = opts.hostname !== global.location.hostname || +      port !== opts.port; +    this.xs = opts.secure !== isSSL; +  } +} + +/** + * Inherits from Polling. + */ + +inherit(XHR, Polling); + +/** + * XHR supports binary + */ + +XHR.prototype.supportsBinary = true; + +/** + * Creates a request. + * + * @param {String} method + * @api private + */ + +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; +  opts.enablesXDR = this.enablesXDR; + +  // SSL options for Node.js client +  opts.pfx = this.pfx; +  opts.key = this.key; +  opts.passphrase = this.passphrase; +  opts.cert = this.cert; +  opts.ca = this.ca; +  opts.ciphers = this.ciphers; +  opts.rejectUnauthorized = this.rejectUnauthorized; +  opts.requestTimeout = this.requestTimeout; + +  // other options for Node.js client +  opts.extraHeaders = this.extraHeaders; + +  return new Request(opts); +}; + +/** + * Sends data. + * + * @param {String} data to send. + * @param {Function} called upon flush. + * @api private + */ + +XHR.prototype.doWrite = function (data, fn) { +  var isBinary = typeof data !== 'string' && data !== undefined; +  var req = this.request({ method: 'POST', data: data, isBinary: isBinary }); +  var self = this; +  req.on('success', fn); +  req.on('error', function (err) { +    self.onError('xhr post error', err); +  }); +  this.sendXhr = req; +}; + +/** + * Starts a poll cycle. + * + * @api private + */ + +XHR.prototype.doPoll = function () { +  debug('xhr poll'); +  var req = this.request(); +  var self = this; +  req.on('data', function (data) { +    self.onData(data); +  }); +  req.on('error', function (err) { +    self.onError('xhr poll error', err); +  }); +  this.pollXhr = req; +}; + +/** + * Request constructor + * + * @param {Object} options + * @api public + */ + +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.isBinary = opts.isBinary; +  this.supportsBinary = opts.supportsBinary; +  this.enablesXDR = opts.enablesXDR; +  this.requestTimeout = opts.requestTimeout; + +  // SSL options for Node.js client +  this.pfx = opts.pfx; +  this.key = opts.key; +  this.passphrase = opts.passphrase; +  this.cert = opts.cert; +  this.ca = opts.ca; +  this.ciphers = opts.ciphers; +  this.rejectUnauthorized = opts.rejectUnauthorized; + +  // other options for Node.js client +  this.extraHeaders = opts.extraHeaders; + +  this.create(); +} + +/** + * Mix in `Emitter`. + */ + +Emitter(Request.prototype); + +/** + * Creates the XHR object and sends the request. + * + * @api private + */ + +Request.prototype.create = function () { +  var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR }; + +  // SSL options for Node.js client +  opts.pfx = this.pfx; +  opts.key = this.key; +  opts.passphrase = this.passphrase; +  opts.cert = this.cert; +  opts.ca = this.ca; +  opts.ciphers = this.ciphers; +  opts.rejectUnauthorized = this.rejectUnauthorized; + +  var xhr = this.xhr = new XMLHttpRequest(opts); +  var self = this; + +  try { +    debug('xhr open %s: %s', this.method, this.uri); +    xhr.open(this.method, this.uri, this.async); +    try { +      if (this.extraHeaders) { +        xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true); +        for (var i in this.extraHeaders) { +          if (this.extraHeaders.hasOwnProperty(i)) { +            xhr.setRequestHeader(i, this.extraHeaders[i]); +          } +        } +      } +    } catch (e) {} + +    if ('POST' === this.method) { +      try { +        if (this.isBinary) { +          xhr.setRequestHeader('Content-type', 'application/octet-stream'); +        } else { +          xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); +        } +      } catch (e) {} +    } + +    try { +      xhr.setRequestHeader('Accept', '*/*'); +    } catch (e) {} + +    // ie6 check +    if ('withCredentials' in xhr) { +      xhr.withCredentials = true; +    } + +    if (this.requestTimeout) { +      xhr.timeout = this.requestTimeout; +    } + +    if (this.hasXDR()) { +      xhr.onload = function () { +        self.onLoad(); +      }; +      xhr.onerror = function () { +        self.onError(xhr.responseText); +      }; +    } else { +      xhr.onreadystatechange = function () { +        if (xhr.readyState === 2) { +          var contentType; +          try { +            contentType = xhr.getResponseHeader('Content-Type'); +          } catch (e) {} +          if (contentType === 'application/octet-stream') { +            xhr.responseType = 'arraybuffer'; +          } +        } +        if (4 !== xhr.readyState) return; +        if (200 === xhr.status || 1223 === xhr.status) { +          self.onLoad(); +        } else { +          // make sure the `error` event handler that's user-set +          // does not throw in the same tick and gets caught here +          setTimeout(function () { +            self.onError(xhr.status); +          }, 0); +        } +      }; +    } + +    debug('xhr data %s', this.data); +    xhr.send(this.data); +  } catch (e) { +    // Need to defer since .create() is called directly fhrom the constructor +    // and thus the 'error' event can only be only bound *after* this exception +    // occurs.  Therefore, also, we cannot throw here at all. +    setTimeout(function () { +      self.onError(e); +    }, 0); +    return; +  } + +  if (global.document) { +    this.index = Request.requestsCount++; +    Request.requests[this.index] = this; +  } +}; + +/** + * Called upon successful response. + * + * @api private + */ + +Request.prototype.onSuccess = function () { +  this.emit('success'); +  this.cleanup(); +}; + +/** + * Called if we have data. + * + * @api private + */ + +Request.prototype.onData = function (data) { +  this.emit('data', data); +  this.onSuccess(); +}; + +/** + * Called upon error. + * + * @api private + */ + +Request.prototype.onError = function (err) { +  this.emit('error', err); +  this.cleanup(true); +}; + +/** + * Cleans up house. + * + * @api private + */ + +Request.prototype.cleanup = function (fromError) { +  if ('undefined' === typeof this.xhr || null === this.xhr) { +    return; +  } +  // xmlhttprequest +  if (this.hasXDR()) { +    this.xhr.onload = this.xhr.onerror = empty; +  } else { +    this.xhr.onreadystatechange = empty; +  } + +  if (fromError) { +    try { +      this.xhr.abort(); +    } catch (e) {} +  } + +  if (global.document) { +    delete Request.requests[this.index]; +  } + +  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 || this.xhr.responseText; +    } else { +      data = this.xhr.responseText; +    } +  } 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 && this.enablesXDR; +}; + +/** + * Aborts the request. + * + * @api public + */ + +Request.prototype.abort = function () { +  this.cleanup(); +}; + +/** + * Aborts pending requests when unloading the window. This is needed to prevent + * memory leaks (e.g. when using IE) and to ensure that no spurious error is + * emitted. + */ + +Request.requestsCount = 0; +Request.requests = {}; + +if (global.document) { +  if (global.attachEvent) { +    global.attachEvent('onunload', unloadHandler); +  } else if (global.addEventListener) { +    global.addEventListener('beforeunload', unloadHandler, false); +  } +} + +function unloadHandler () { +  for (var i in Request.requests) { +    if (Request.requests.hasOwnProperty(i)) { +      Request.requests[i].abort(); +    } +  } +} | 
