You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

186 lines
5.0 KiB

4 years ago
  1. var Stream = require('stream').Stream,
  2. util = require('util'),
  3. driver = require('websocket-driver'),
  4. EventTarget = require('./api/event_target'),
  5. Event = require('./api/event');
  6. var API = function(options) {
  7. options = options || {};
  8. driver.validateOptions(options, ['headers', 'extensions', 'maxLength', 'ping', 'proxy', 'tls', 'ca']);
  9. this.readable = this.writable = true;
  10. var headers = options.headers;
  11. if (headers) {
  12. for (var name in headers) this._driver.setHeader(name, headers[name]);
  13. }
  14. var extensions = options.extensions;
  15. if (extensions) {
  16. [].concat(extensions).forEach(this._driver.addExtension, this._driver);
  17. }
  18. this._ping = options.ping;
  19. this._pingId = 0;
  20. this.readyState = API.CONNECTING;
  21. this.bufferedAmount = 0;
  22. this.protocol = '';
  23. this.url = this._driver.url;
  24. this.version = this._driver.version;
  25. var self = this;
  26. this._driver.on('open', function(e) { self._open() });
  27. this._driver.on('message', function(e) { self._receiveMessage(e.data) });
  28. this._driver.on('close', function(e) { self._beginClose(e.reason, e.code) });
  29. this._driver.on('error', function(error) {
  30. self._emitError(error.message);
  31. });
  32. this.on('error', function() {});
  33. this._driver.messages.on('drain', function() {
  34. self.emit('drain');
  35. });
  36. if (this._ping)
  37. this._pingTimer = setInterval(function() {
  38. self._pingId += 1;
  39. self.ping(self._pingId.toString());
  40. }, this._ping * 1000);
  41. this._configureStream();
  42. if (!this._proxy) {
  43. this._stream.pipe(this._driver.io);
  44. this._driver.io.pipe(this._stream);
  45. }
  46. };
  47. util.inherits(API, Stream);
  48. API.CONNECTING = 0;
  49. API.OPEN = 1;
  50. API.CLOSING = 2;
  51. API.CLOSED = 3;
  52. var instance = {
  53. write: function(data) {
  54. return this.send(data);
  55. },
  56. end: function(data) {
  57. if (data !== undefined) this.send(data);
  58. this.close();
  59. },
  60. pause: function() {
  61. return this._driver.messages.pause();
  62. },
  63. resume: function() {
  64. return this._driver.messages.resume();
  65. },
  66. send: function(data) {
  67. if (this.readyState > API.OPEN) return false;
  68. if (!(data instanceof Buffer)) data = String(data);
  69. return this._driver.messages.write(data);
  70. },
  71. ping: function(message, callback) {
  72. if (this.readyState > API.OPEN) return false;
  73. return this._driver.ping(message, callback);
  74. },
  75. close: function(code, reason) {
  76. if (code === undefined) code = 1000;
  77. if (reason === undefined) reason = '';
  78. if (code !== 1000 && (code < 3000 || code > 4999))
  79. throw new Error("Failed to execute 'close' on WebSocket: " +
  80. "The code must be either 1000, or between 3000 and 4999. " +
  81. code + " is neither.");
  82. if (this.readyState !== API.CLOSED) this.readyState = API.CLOSING;
  83. this._driver.close(reason, code);
  84. },
  85. _configureStream: function() {
  86. var self = this;
  87. this._stream.setTimeout(0);
  88. this._stream.setNoDelay(true);
  89. ['close', 'end'].forEach(function(event) {
  90. this._stream.on(event, function() { self._finalizeClose() });
  91. }, this);
  92. this._stream.on('error', function(error) {
  93. self._emitError('Network error: ' + self.url + ': ' + error.message);
  94. self._finalizeClose();
  95. });
  96. },
  97. _open: function() {
  98. if (this.readyState !== API.CONNECTING) return;
  99. this.readyState = API.OPEN;
  100. this.protocol = this._driver.protocol || '';
  101. var event = new Event('open');
  102. event.initEvent('open', false, false);
  103. this.dispatchEvent(event);
  104. },
  105. _receiveMessage: function(data) {
  106. if (this.readyState > API.OPEN) return false;
  107. if (this.readable) this.emit('data', data);
  108. var event = new Event('message', {data: data});
  109. event.initEvent('message', false, false);
  110. this.dispatchEvent(event);
  111. },
  112. _emitError: function(message) {
  113. if (this.readyState >= API.CLOSING) return;
  114. var event = new Event('error', {message: message});
  115. event.initEvent('error', false, false);
  116. this.dispatchEvent(event);
  117. },
  118. _beginClose: function(reason, code) {
  119. if (this.readyState === API.CLOSED) return;
  120. this.readyState = API.CLOSING;
  121. this._closeParams = [reason, code];
  122. if (this._stream) {
  123. this._stream.end();
  124. if (!this._stream.readable) this._finalizeClose();
  125. }
  126. },
  127. _finalizeClose: function() {
  128. if (this.readyState === API.CLOSED) return;
  129. this.readyState = API.CLOSED;
  130. if (this._pingTimer) clearInterval(this._pingTimer);
  131. if (this._stream) this._stream.end();
  132. if (this.readable) this.emit('end');
  133. this.readable = this.writable = false;
  134. var reason = this._closeParams ? this._closeParams[0] : '',
  135. code = this._closeParams ? this._closeParams[1] : 1006;
  136. var event = new Event('close', {code: code, reason: reason});
  137. event.initEvent('close', false, false);
  138. this.dispatchEvent(event);
  139. }
  140. };
  141. for (var method in instance) API.prototype[method] = instance[method];
  142. for (var key in EventTarget) API.prototype[key] = EventTarget[key];
  143. module.exports = API;