Since you already know that all current WebSocket version will be obsolete very soon and you're using a WebSocket server which supports the old 75 draft, it's fairly trivial to make one if you already have some server code lying around, so no need for the "security" header stuff in 76.
Disclaimer: This thing had only 5 minute of testing or so but it should work for the most part.
Epic wall of code follows
var net = require('net'); function WebSocket(host, port, encoder, decoder) { this.encoder = encoder || function(data){return data.toString()}; this.decoder = decoder || function(data){return data}; this.socket = net.createConnection(port, host); this.connected = false; this.header = 0; this.bytesSend = 0; this.dataFrames = []; this.dataState = 0; var that = this; process.nextTick(function() { that.init(host, port); }); } // Prototype ------------------------------------------------------------------- WebSocket.prototype = { onConnect: function() {console.log('connect');}, onClose: function() {console.log('close');}, onData: function(data) {console.log(data)}, init: function(host, port) { var that = this; this.socket.addListener('connect', function() { var data ='GET / HTTP/1.1\r\n' + 'Host: ' + host + ':' + port + '\r\n' + 'Origin: websocket.node.js\r\n' + 'Connection: Upgrade\r\n' + 'Upgrade: WebSocket\r\n\r\n'; that.socket.write(data, 'ascii'); that.socket.flush(); }); this.socket.addListener('data', function(data) {that.read(data);}); this.socket.addListener('end', function() {that.onClose();}); this.socket.addListener('error', function(e) {console.log(e.message);that.close();}); }, send: function(data, encoded) { if (this.connected) { return this.write(encoded ? data : this.encoder(data)); } else { return 0; } }, close: function() { if (this.connected) { this.connected = false; this.write(null); this.socket.end(); this.socket.destroy(); } }, read: function read(data) { for(var i = 0, l = data.length; i < l; i++) { var b = data[i]; if (this.header < 4) { if ((this.header === 0 || this.header === 2) && b === 0x0d) { this.header++; } else if ((this.header === 1 || this.header === 3) && b === 0x0a) { this.header++; } else { this.header = 0; } if (this.header === 4) { this.connected = true; this.onConnect(); this.header = 5; } } else { if (this.dataState === 0) { this.dataState = b & 0x80 === 0x80 ? 2 : 1; // Low bit frame } else if (this.dataState === 1) { if (b === 0xff) { var buffer = new Buffer(this.dataFrames); this.dataFrames = []; this.dataState = 0; if (!this.message(buffer.toString('utf8', 0, buffer.length))) { this.send({error: 'Invalid Message.'}); this.close(); return; } } else { this.dataFrames.push(b); } // Unused high bit frames } else if (this.dataState === 2) { if (b === 0x00) { this.close(); } } } } }, write: function(data) { var bytes = 0; if (!this.socket.writable) { return bytes; } try { this.socket.write('\x00', 'binary'); if (typeof data === 'string') { this.socket.write(data, 'utf8'); bytes += Buffer.byteLength(data); } this.socket.write('\xff', 'binary'); this.socket.flush(); bytes += 2; } catch(e) {} this.bytesSend += bytes; return bytes; }, message: function(msg) { if (this.decoder) { try { msg = this.decoder(msg); } catch(e) { this.close(); return false; } } this.onData(msg); return true; } };
And here we set it up:
var bison = require('bison'); // automatically do some encoding/decoding magic var test = new WebSocket('localhost', 28785, bison.encode, bison.decode); test.onConnect = function() { }; test.onData = function(data) { };
Feel free to ask questions in the comments.
PS: For info on how it works, read the spec :P