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