| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 | 'use strict';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;API.CLOSE_TIMEOUT = 30000;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.CLOSING) {      var self = this;      this._closeTimer = setTimeout(function() {        self._beginClose('', 1006);      }, API.CLOSE_TIMEOUT);    }    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.destroy();      if (!this._stream.readable) this._finalizeClose();    }  },  _finalizeClose: function() {    if (this.readyState === API.CLOSED) return;    this.readyState = API.CLOSED;    if (this._closeTimer) clearTimeout(this._closeTimer);    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;
 |