187 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| var Stream      = require('stream').Stream,
 | |
|     util        = require('util'),
 | |
|     driver      = require('websocket-driver'),
 | |
|     EventTarget = require('./api/event_target'),
 | |
|     Event       = require('./api/event');
 | |
| 
 | |
| var API = function(options) {
 | |
|   options = options || {};
 | |
|   driver.validateOptions(options, ['headers', 'extensions', 'maxLength', 'ping', 'proxy', 'tls', 'ca']);
 | |
| 
 | |
|   this.readable = this.writable = true;
 | |
| 
 | |
|   var headers = options.headers;
 | |
|   if (headers) {
 | |
|     for (var name in headers) this._driver.setHeader(name, headers[name]);
 | |
|   }
 | |
| 
 | |
|   var extensions = options.extensions;
 | |
|   if (extensions) {
 | |
|     [].concat(extensions).forEach(this._driver.addExtension, this._driver);
 | |
|   }
 | |
| 
 | |
|   this._ping          = options.ping;
 | |
|   this._pingId        = 0;
 | |
|   this.readyState     = API.CONNECTING;
 | |
|   this.bufferedAmount = 0;
 | |
|   this.protocol       = '';
 | |
|   this.url            = this._driver.url;
 | |
|   this.version        = this._driver.version;
 | |
| 
 | |
|   var self = this;
 | |
| 
 | |
|   this._driver.on('open',    function(e) { self._open() });
 | |
|   this._driver.on('message', function(e) { self._receiveMessage(e.data) });
 | |
|   this._driver.on('close',   function(e) { self._beginClose(e.reason, e.code) });
 | |
| 
 | |
|   this._driver.on('error', function(error) {
 | |
|     self._emitError(error.message);
 | |
|   });
 | |
|   this.on('error', function() {});
 | |
| 
 | |
|   this._driver.messages.on('drain', function() {
 | |
|     self.emit('drain');
 | |
|   });
 | |
| 
 | |
|   if (this._ping)
 | |
|     this._pingTimer = setInterval(function() {
 | |
|       self._pingId += 1;
 | |
|       self.ping(self._pingId.toString());
 | |
|     }, this._ping * 1000);
 | |
| 
 | |
|   this._configureStream();
 | |
| 
 | |
|   if (!this._proxy) {
 | |
|     this._stream.pipe(this._driver.io);
 | |
|     this._driver.io.pipe(this._stream);
 | |
|   }
 | |
| };
 | |
| util.inherits(API, Stream);
 | |
| 
 | |
| API.CONNECTING = 0;
 | |
| API.OPEN       = 1;
 | |
| API.CLOSING    = 2;
 | |
| API.CLOSED     = 3;
 | |
| 
 | |
| var instance = {
 | |
|   write: function(data) {
 | |
|     return this.send(data);
 | |
|   },
 | |
| 
 | |
|   end: function(data) {
 | |
|     if (data !== undefined) this.send(data);
 | |
|     this.close();
 | |
|   },
 | |
| 
 | |
|   pause: function() {
 | |
|     return this._driver.messages.pause();
 | |
|   },
 | |
| 
 | |
|   resume: function() {
 | |
|     return this._driver.messages.resume();
 | |
|   },
 | |
| 
 | |
|   send: function(data) {
 | |
|     if (this.readyState > API.OPEN) return false;
 | |
|     if (!(data instanceof Buffer)) data = String(data);
 | |
|     return this._driver.messages.write(data);
 | |
|   },
 | |
| 
 | |
|   ping: function(message, callback) {
 | |
|     if (this.readyState > API.OPEN) return false;
 | |
|     return this._driver.ping(message, callback);
 | |
|   },
 | |
| 
 | |
|   close: function(code, reason) {
 | |
|     if (code === undefined) code = 1000;
 | |
|     if (reason === undefined) reason = '';
 | |
| 
 | |
|     if (code !== 1000 && (code < 3000 || code > 4999))
 | |
|       throw new Error("Failed to execute 'close' on WebSocket: " +
 | |
|                       "The code must be either 1000, or between 3000 and 4999. " +
 | |
|                       code + " is neither.");
 | |
| 
 | |
|     if (this.readyState !== API.CLOSED) this.readyState = API.CLOSING;
 | |
|     this._driver.close(reason, code);
 | |
|   },
 | |
| 
 | |
|   _configureStream: function() {
 | |
|     var self = this;
 | |
| 
 | |
|     this._stream.setTimeout(0);
 | |
|     this._stream.setNoDelay(true);
 | |
| 
 | |
|     ['close', 'end'].forEach(function(event) {
 | |
|       this._stream.on(event, function() { self._finalizeClose() });
 | |
|     }, this);
 | |
| 
 | |
|     this._stream.on('error', function(error) {
 | |
|       self._emitError('Network error: ' + self.url + ': ' + error.message);
 | |
|       self._finalizeClose();
 | |
|     });
 | |
|   },
 | |
| 
 | |
|  _open: function() {
 | |
|     if (this.readyState !== API.CONNECTING) return;
 | |
| 
 | |
|     this.readyState = API.OPEN;
 | |
|     this.protocol = this._driver.protocol || '';
 | |
| 
 | |
|     var event = new Event('open');
 | |
|     event.initEvent('open', false, false);
 | |
|     this.dispatchEvent(event);
 | |
|   },
 | |
| 
 | |
|   _receiveMessage: function(data) {
 | |
|     if (this.readyState > API.OPEN) return false;
 | |
| 
 | |
|     if (this.readable) this.emit('data', data);
 | |
| 
 | |
|     var event = new Event('message', {data: data});
 | |
|     event.initEvent('message', false, false);
 | |
|     this.dispatchEvent(event);
 | |
|   },
 | |
| 
 | |
|   _emitError: function(message) {
 | |
|     if (this.readyState >= API.CLOSING) return;
 | |
| 
 | |
|     var event = new Event('error', {message: message});
 | |
|     event.initEvent('error', false, false);
 | |
|     this.dispatchEvent(event);
 | |
|   },
 | |
| 
 | |
|   _beginClose: function(reason, code) {
 | |
|     if (this.readyState === API.CLOSED) return;
 | |
|     this.readyState = API.CLOSING;
 | |
|     this._closeParams = [reason, code];
 | |
| 
 | |
|     if (this._stream) {
 | |
|       this._stream.end();
 | |
|       if (!this._stream.readable) this._finalizeClose();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _finalizeClose: function() {
 | |
|     if (this.readyState === API.CLOSED) return;
 | |
|     this.readyState = API.CLOSED;
 | |
| 
 | |
|     if (this._pingTimer) clearInterval(this._pingTimer);
 | |
|     if (this._stream) this._stream.end();
 | |
| 
 | |
|     if (this.readable) this.emit('end');
 | |
|     this.readable = this.writable = false;
 | |
| 
 | |
|     var reason = this._closeParams ? this._closeParams[0] : '',
 | |
|         code   = this._closeParams ? this._closeParams[1] : 1006;
 | |
| 
 | |
|     var event = new Event('close', {code: code, reason: reason});
 | |
|     event.initEvent('close', false, false);
 | |
|     this.dispatchEvent(event);
 | |
|   }
 | |
| };
 | |
| 
 | |
| for (var method in instance) API.prototype[method] = instance[method];
 | |
| for (var key in EventTarget) API.prototype[key] = EventTarget[key];
 | |
| 
 | |
| module.exports = API;
 |