194 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| '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;
 |