Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turned websocket into a class #424

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 97 additions & 114 deletions lib/W3CWebSocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,81 +14,123 @@
* limitations under the License.
***********************************************************************/

var WebSocketClient = require('./WebSocketClient');
var toBuffer = require('typedarray-to-buffer');
var yaeti = require('yaeti');

const WebSocketClient = require('./WebSocketClient');
const toBuffer = require('typedarray-to-buffer');
const yaeti = require('yaeti');

const CONNECTING = 0;
const OPEN = 1;
const CLOSING = 2;
const CLOSED = 3;

class W3CWebSocket extends yaeti.EventTarget {
constructor (url, protocols, origin, headers, requestOptions, clientConfig) {
super()

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.

// Sanitize clientConfig.
clientConfig = clientConfig || {};
clientConfig.assembleFragments = true; // Required in the W3C API.
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.

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

// The WebSocketConnection instance.
this._connection = undefined;
// WebSocketClient instance.
this._client = new WebSocketClient(clientConfig)

// WebSocketClient instance.
this._client = new WebSocketClient(clientConfig);
this._client.on('connect', (connection) => {
onConnect.call(this, connection)
})

this._client.on('connect', function(connection) {
onConnect.call(self, connection);
});
this._client.on('connectFailed', () => {
onConnectFailed.call(this)
})

this._client.on('connectFailed', function() {
onConnectFailed.call(self);
});

this._client.connect(url, protocols, origin, headers, requestOptions);
}
this._client.connect(url, protocols, origin, headers, requestOptions)
}

send(data) {
if (this._readyState !== OPEN) {
throw new Error('cannot call send() while not connected')
}

// 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; } }
});
// Text.
if (typeof data === 'string' || data instanceof String) {
this._connection.sendUTF(data)
}

// Binary.
else {
// Node Buffer.
if (data instanceof Buffer) {
this._connection.sendBytes(data)
}

// 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');
// 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)
}
this._binaryType = type;
}
}
});

close(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
}
}

// Expose W3C read only attributes.
get url() { return this._url }
get readyState() { return this._readyState }
get protocol() { return this._protocol }
get extensions() { return this._extensions }
get bufferedAmount() { return this._bufferedAmount }

// Expose W3C write/read attributes.
get binaryType() { return this._binaryType }
set binaryType(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) {
Expand All @@ -105,70 +147,9 @@ Object.defineProperties(W3CWebSocket.prototype, {
});
});


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');

Expand Down Expand Up @@ -255,3 +236,5 @@ function destroy() {
this._connection.removeAllListeners();
}
}

module.exports = W3CWebSocket;