From c26036aa8ae4c1167c6b4fe26870800c359a4c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Lal?= Date: Wed, 21 Dec 2016 07:12:21 -0500 Subject: Import node-websocket_1.0.23.orig.tar.gz [dgit import orig node-websocket_1.0.23.orig.tar.gz] --- lib/W3CWebSocket.js | 257 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 lib/W3CWebSocket.js (limited to 'lib/W3CWebSocket.js') diff --git a/lib/W3CWebSocket.js b/lib/W3CWebSocket.js new file mode 100644 index 0000000..4305fb6 --- /dev/null +++ b/lib/W3CWebSocket.js @@ -0,0 +1,257 @@ +/************************************************************************ + * Copyright 2010-2015 Brian McKelvey. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***********************************************************************/ + +var WebSocketClient = require('./WebSocketClient'); +var toBuffer = require('typedarray-to-buffer'); +var yaeti = require('yaeti'); + + +const CONNECTING = 0; +const OPEN = 1; +const CLOSING = 2; +const CLOSED = 3; + + +module.exports = W3CWebSocket; + + +function W3CWebSocket(url, protocols, origin, headers, requestOptions, clientConfig) { + // Make this an EventTarget. + yaeti.EventTarget.call(this); + + // Sanitize clientConfig. + clientConfig = clientConfig || {}; + clientConfig.assembleFragments = true; // Required in the W3C API. + + var self = this; + + this._url = url; + this._readyState = CONNECTING; + this._protocol = undefined; + this._extensions = ''; + this._bufferedAmount = 0; // Hack, always 0. + this._binaryType = 'arraybuffer'; // TODO: Should be 'blob' by default, but Node has no Blob. + + // The WebSocketConnection instance. + this._connection = undefined; + + // WebSocketClient instance. + this._client = new WebSocketClient(clientConfig); + + this._client.on('connect', function(connection) { + onConnect.call(self, connection); + }); + + this._client.on('connectFailed', function() { + onConnectFailed.call(self); + }); + + this._client.connect(url, protocols, origin, headers, requestOptions); +} + + +// Expose W3C read only attributes. +Object.defineProperties(W3CWebSocket.prototype, { + url: { get: function() { return this._url; } }, + readyState: { get: function() { return this._readyState; } }, + protocol: { get: function() { return this._protocol; } }, + extensions: { get: function() { return this._extensions; } }, + bufferedAmount: { get: function() { return this._bufferedAmount; } } +}); + + +// Expose W3C write/read attributes. +Object.defineProperties(W3CWebSocket.prototype, { + binaryType: { + get: function() { + return this._binaryType; + }, + set: function(type) { + // TODO: Just 'arraybuffer' supported. + if (type !== 'arraybuffer') { + throw new SyntaxError('just "arraybuffer" type allowed for "binaryType" attribute'); + } + this._binaryType = type; + } + } +}); + + +// Expose W3C readyState constants into the WebSocket instance as W3C states. +[['CONNECTING',CONNECTING], ['OPEN',OPEN], ['CLOSING',CLOSING], ['CLOSED',CLOSED]].forEach(function(property) { + Object.defineProperty(W3CWebSocket.prototype, property[0], { + get: function() { return property[1]; } + }); +}); + +// Also expone W3C readyState constants into the WebSocket class (not defined by the W3C, +// but there are so many libs relying on them). +[['CONNECTING',CONNECTING], ['OPEN',OPEN], ['CLOSING',CLOSING], ['CLOSED',CLOSED]].forEach(function(property) { + Object.defineProperty(W3CWebSocket, property[0], { + get: function() { return property[1]; } + }); +}); + + +W3CWebSocket.prototype.send = function(data) { + if (this._readyState !== OPEN) { + throw new Error('cannot call send() while not connected'); + } + + // Text. + if (typeof data === 'string' || data instanceof String) { + this._connection.sendUTF(data); + } + // Binary. + else { + // Node Buffer. + if (data instanceof Buffer) { + this._connection.sendBytes(data); + } + // If ArrayBuffer or ArrayBufferView convert it to Node Buffer. + else if (data.byteLength || data.byteLength === 0) { + data = toBuffer(data); + this._connection.sendBytes(data); + } + else { + throw new Error('unknown binary data:', data); + } + } +}; + + +W3CWebSocket.prototype.close = function(code, reason) { + switch(this._readyState) { + case CONNECTING: + // NOTE: We don't have the WebSocketConnection instance yet so no + // way to close the TCP connection. + // Artificially invoke the onConnectFailed event. + onConnectFailed.call(this); + // And close if it connects after a while. + this._client.on('connect', function(connection) { + if (code) { + connection.close(code, reason); + } else { + connection.close(); + } + }); + break; + case OPEN: + this._readyState = CLOSING; + if (code) { + this._connection.close(code, reason); + } else { + this._connection.close(); + } + break; + case CLOSING: + case CLOSED: + break; + } +}; + + +/** + * Private API. + */ + + +function createCloseEvent(code, reason) { + var event = new yaeti.Event('close'); + + event.code = code; + event.reason = reason; + event.wasClean = (typeof code === 'undefined' || code === 1000); + + return event; +} + + +function createMessageEvent(data) { + var event = new yaeti.Event('message'); + + event.data = data; + + return event; +} + + +function onConnect(connection) { + var self = this; + + this._readyState = OPEN; + this._connection = connection; + this._protocol = connection.protocol; + this._extensions = connection.extensions; + + this._connection.on('close', function(code, reason) { + onClose.call(self, code, reason); + }); + + this._connection.on('message', function(msg) { + onMessage.call(self, msg); + }); + + this.dispatchEvent(new yaeti.Event('open')); +} + + +function onConnectFailed() { + destroy.call(this); + this._readyState = CLOSED; + + try { + this.dispatchEvent(new yaeti.Event('error')); + } finally { + this.dispatchEvent(createCloseEvent(1006, 'connection failed')); + } +} + + +function onClose(code, reason) { + destroy.call(this); + this._readyState = CLOSED; + + this.dispatchEvent(createCloseEvent(code, reason || '')); +} + + +function onMessage(message) { + if (message.utf8Data) { + this.dispatchEvent(createMessageEvent(message.utf8Data)); + } + else if (message.binaryData) { + // Must convert from Node Buffer to ArrayBuffer. + // TODO: or to a Blob (which does not exist in Node!). + if (this.binaryType === 'arraybuffer') { + var buffer = message.binaryData; + var arraybuffer = new ArrayBuffer(buffer.length); + var view = new Uint8Array(arraybuffer); + for (var i=0, len=buffer.length; i