I'm trying to unit test my websocket on node.js and want to mock out a websocket client. I could create a HTML file that just connects to my server but then I can't run a single test on the server.
How would I go about (using either http.Client
or net.Stream
) to create a websocket client and have it interact with my server.
I'm targetting the (soon to be dead) draft 76 of the websocket spec.
The server side implementation I'm using is this
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With