added networking tests

This commit is contained in:
logsol 2012-06-16 22:03:03 +02:00
parent bcde1d3171
commit 620f2626a3
506 changed files with 228337 additions and 0 deletions

View file

@ -0,0 +1,27 @@
Copyright (c) 2010, Peter Griess <pg@std.in>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of node-websocket-client nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,22 @@
# This makefile exists to help run tests.
#
# If TEST_UNIX is a non-empty value, runs tests for UNIX sockets. This
# functionality is not in node-websocket-server at the moment.
.PHONY: test
all: test test-unix
test:
for f in `ls -1 test/test-*.js | grep -v unix` ; do \
echo $$f ; \
node $$f ; \
done
test-unix:
if [[ -n "$$TEST_UNIX" ]] ; then \
for f in `ls -1 test/test-*.js | grep unix` ; do \
echo $$f ; \
node $$f ; \
done \
fi

View file

@ -0,0 +1,41 @@
A prototype [Web Socket](http://www.whatwg.org/specs/web-socket-protocol/)
client implementation for [node.js](http://nodejs.org).
Tested with
[miksago/node-websocket-server](http://github.com/miksago/node-websocket-server)
v1.2.00.
Requires [nodejs](http://nodejs.org) 0.1.98 or later.
## Installation
Install this using `npm` as follows
npm install websocket-client
... or just dump `lib/websocket.js` in your `$NODE_PATH`.
## Usage
var sys = require('sys');
var WebSocket = require('websocket').WebSocket;
var ws = new WebSocket('ws://localhost:8000/biff', 'borf');
ws.addListener('data', function(buf) {
sys.debug('Got data: ' + sys.inspect(buf));
});
ws.onmessage = function(m) {
sys.debug('Got message: ' + m);
}
## API
This supports the `send()` and `onmessage()` APIs. The `WebSocket` object will
also emit `data` events that are node `Buffer` objects, in case you want to
work with something lower-level than strings.
## Transports
Multiple transports are supported, indicated by the scheme provided to the
`WebSocket` constructor. `ws://` is a standard TCP-based Web Socket;
`ws+unix://` allows connection to a UNIX socket at the given path.

View file

@ -0,0 +1,12 @@
var sys = require('sys');
var WebSocket = require('../lib/websocket').WebSocket;
var ws = new WebSocket('ws+unix://' + process.argv[2], 'boffo');
ws.addListener('message', function(d) {
sys.debug('Received message: ' + d.toString('utf8'));
});
ws.addListener('open', function() {
ws.send('This is a message', 1);
});

View file

@ -0,0 +1,10 @@
var sys = require('sys');
var WebSocket = require('../lib/websocket').WebSocket;
var ws = new WebSocket('ws://localhost:8000/biff', 'borf');
ws.addListener('data', function(buf) {
sys.debug('Got data: ' + sys.inspect(buf));
});
ws.onmessage = function(m) {
sys.debug('Got message: ' + m);
}

View file

@ -0,0 +1,13 @@
var sys = require('sys');
var ws = require('websocket-server/ws');
var srv = ws.createServer({ debug : true});
srv.addListener('connection', function(s) {
sys.debug('Got a connection!');
s._req.socket.addListener('fd', function(fd) {
sys.debug('Got an fd: ' + fd);
});
});
srv.listen(process.argv[2]);

View file

@ -0,0 +1,617 @@
var assert = require('assert');
var buffer = require('buffer');
var crypto = require('crypto');
var events = require('events');
var http = require('http');
var net = require('net');
var urllib = require('url');
var sys = require('util');
var FRAME_NO = 0;
var FRAME_LO = 1;
var FRAME_HI = 2;
// Values for readyState as per the W3C spec
var CONNECTING = 0;
var OPEN = 1;
var CLOSING = 2;
var CLOSED = 3;
var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
var debug = (debugLevel & 0x4) ?
function() { sys.error.apply(this, arguments); } :
function() { };
// Generate a Sec-WebSocket-* value
var createSecretKey = function() {
// How many spaces will we be inserting?
var numSpaces = 1 + Math.floor(Math.random() * 12);
assert.ok(1 <= numSpaces && numSpaces <= 12);
// What is the numerical value of our key?
var keyVal = (Math.floor(
Math.random() * (4294967295 / numSpaces)
) * numSpaces);
// Our string starts with a string representation of our key
var s = keyVal.toString();
// Insert 'numChars' worth of noise in the character ranges
// [0x21, 0x2f] (14 characters) and [0x3a, 0x7e] (68 characters)
var numChars = 1 + Math.floor(Math.random() * 12);
assert.ok(1 <= numChars && numChars <= 12);
for (var i = 0; i < numChars; i++) {
var pos = Math.floor(Math.random() * s.length + 1);
var c = Math.floor(Math.random() * (14 + 68));
c = (c <= 14) ?
String.fromCharCode(c + 0x21) :
String.fromCharCode((c - 14) + 0x3a);
s = s.substring(0, pos) + c + s.substring(pos, s.length);
}
// We shoudln't have any spaces in our value until we insert them
assert.equal(s.indexOf(' '), -1);
// Insert 'numSpaces' worth of spaces
for (var i = 0; i < numSpaces; i++) {
var pos = Math.floor(Math.random() * (s.length - 1)) + 1;
s = s.substring(0, pos) + ' ' + s.substring(pos, s.length);
}
assert.notEqual(s.charAt(0), ' ');
assert.notEqual(s.charAt(s.length), ' ');
return s;
};
// Generate a challenge sequence
var createChallenge = function() {
var c = '';
for (var i = 0; i < 8; i++) {
c += String.fromCharCode(Math.floor(Math.random() * 255));
}
return c;
};
// Get the value of a secret key string
//
// This strips non-digit values and divides the result by the number of
// spaces found.
var secretKeyValue = function(sk) {
var ns = 0;
var v = 0;
for (var i = 0; i < sk.length; i++) {
var cc = sk.charCodeAt(i);
if (cc == 0x20) {
ns++;
} else if (0x30 <= cc && cc <= 0x39) {
v = v * 10 + cc - 0x30;
}
}
return Math.floor(v / ns);
}
// Get the to-be-hashed value of a secret key string
//
// This takes the result of secretKeyValue() and encodes it in a big-endian
// byte string
var secretKeyHashValue = function(sk) {
var skv = secretKeyValue(sk);
var hv = '';
hv += String.fromCharCode((skv >> 24) & 0xff);
hv += String.fromCharCode((skv >> 16) & 0xff);
hv += String.fromCharCode((skv >> 8) & 0xff);
hv += String.fromCharCode((skv >> 0) & 0xff);
return hv;
};
// Compute the secret key signature based on two secret key strings and some
// handshaking data.
var computeSecretKeySignature = function(s1, s2, hs) {
assert.equal(hs.length, 8);
var hash = crypto.createHash('md5');
hash.update(secretKeyHashValue(s1));
hash.update(secretKeyHashValue(s2));
hash.update(hs);
return hash.digest('binary');
};
// Return a hex representation of the given binary string; used for debugging
var str2hex = function(str) {
var hexChars = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'
];
var out = '';
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
out += hexChars[(c & 0xf0) >>> 4];
out += hexChars[c & 0x0f];
out += ' ';
}
return out.trim();
};
// Get the scheme for a URL, undefined if none is found
var getUrlScheme = function(url) {
var i = url.indexOf(':');
if (i == -1) {
return undefined;
}
return url.substring(0, i);
};
// Set a constant on the given object
var setConstant = function(obj, name, value) {
Object.defineProperty(obj, name, {
get : function() {
return value;
}
});
};
// WebSocket object
//
// This is intended to conform (mostly) to http://dev.w3.org/html5/websockets/
//
// N.B. Arguments are parsed in the anonymous function at the bottom of the
// constructor.
var WebSocket = function(url, proto, opts) {
events.EventEmitter.call(this);
// Retain a reference to our object
var self = this;
// State of our end of the connection
var readyState = CONNECTING;
// Whether or not the server has sent a close handshake
var serverClosed = false;
// Our underlying net.Stream instance
var stream = undefined;
opts = opts || {
origin : 'http://www.example.com'
};
// Frame parsing functions
//
// These read data from the given buffer starting at the given offset,
// looking for the end of the current frame. If found, the current frame is
// emitted and the function returns. Only a single frame is processed at a
// time.
//
// The number of bytes read to complete a frame is returned, which the
// caller is to use to advance along its buffer. If 0 is returned, no
// completed frame bytes were found, and the caller should probably enqueue
// the buffer as a continuation of the current message. If a complete frame
// is read, the function is responsible for resting 'frameType'.
// Framing data
var frameType = FRAME_NO;
var bufs = [];
var bufsBytes = 0;
// Frame-parsing functions
var frameFuncs = [
// FRAME_NO
function(buf, off) {
if (buf[off] & 0x80) {
frameType = FRAME_HI;
} else {
frameType = FRAME_LO;
}
return 1;
},
// FRAME_LO
function(buf, off) {
debug('frame_lo(' + sys.inspect(buf) + ', ' + off + ')');
// Find the first instance of 0xff, our terminating byte
for (var i = off; i < buf.length && buf[i] != 0xff; i++)
;
// We didn't find a terminating byte
if (i >= buf.length) {
return 0;
}
// We found a terminating byte; collect all bytes into a single buffer
// and emit it
var mb = null;
if (bufs.length == 0) {
mb = buf.slice(off, i);
} else {
mb = new buffer.Buffer(bufsBytes + i);
var mbOff = 0;
bufs.forEach(function(b) {
b.copy(mb, mbOff, 0, b.length);
mbOff += b.length;
});
assert.equal(mbOff, bufsBytes);
// Don't call Buffer.copy() if we're coping 0 bytes. Rather
// than being a no-op, this will trigger a range violation on
// the destination.
if (i > 0) {
buf.copy(mb, mbOff, off, i);
}
// We consumed all of the buffers that we'd been saving; clear
// things out
bufs = [];
bufsBytes = 0;
}
process.nextTick(function() {
var b = mb;
return function() {
var m = b.toString('utf8');
self.emit('data', b);
self.emit('message', m); // wss compat
if (self.onmessage) {
self.onmessage({data: m});
}
};
}());
frameType = FRAME_NO;
return i - off + 1;
},
// FRAME_HI
function(buf, off) {
debug('frame_hi(' + sys.inspect(buf) + ', ' + off + ')');
if (buf[off] !== 0) {
throw new Error('High-byte framing not supported.');
}
serverClosed = true;
return 1;
}
];
// Handle data coming from our socket
var dataListener = function(buf) {
if (buf.length <= 0 || serverClosed) {
return;
}
debug('dataListener(' + sys.inspect(buf) + ')');
var off = 0;
var consumed = 0;
do {
if (frameType < 0 || frameFuncs.length <= frameType) {
throw new Error('Unexpected frame type: ' + frameType);
}
assert.equal(bufs.length === 0, bufsBytes === 0);
assert.ok(off < buf.length);
consumed = frameFuncs[frameType](buf, off);
off += consumed;
} while (!serverClosed && consumed > 0 && off < buf.length);
if (serverClosed) {
serverCloseHandler();
}
if (consumed == 0) {
bufs.push(buf.slice(off, buf.length));
bufsBytes += buf.length - off;
}
};
// Handle incoming file descriptors
var fdListener = function(fd) {
self.emit('fd', fd);
};
// Handle errors from any source (HTTP client, stream, etc)
var errorListener = function(e) {
process.nextTick(function() {
self.emit('wserror', e);
if (self.onerror) {
self.onerror(e);
}
});
};
// Finish the closing process; destroy the socket and tell the application
// that we've closed.
var finishClose = self.finishClose = function() {
readyState = CLOSED;
if (stream) {
stream.end();
stream.destroy();
stream = undefined;
}
process.nextTick(function() {
self.emit('close');
if (self.onclose) {
self.onclose();
}
});
};
// Send a close frame to the server
var sendClose = function() {
assert.equal(OPEN, readyState);
readyState = CLOSING;
stream.write('\xff\x00', 'binary');
};
// Handle a close packet sent from the server
var serverCloseHandler = function() {
assert.ok(serverClosed);
assert.ok(readyState === OPEN || readyState === CLOSING);
bufs = [];
bufsBytes = 0;
// Handle state transitions asynchronously so that we don't change
// readyState before the application has had a chance to process data
// events which are already in the delivery pipeline. For example, a
// 'data' event could be delivered with a readyState of CLOSING if we
// received both frames in the same packet.
process.nextTick(function() {
if (readyState === OPEN) {
sendClose();
}
finishClose();
});
};
// External API
self.close = function(timeout) {
if (readyState === CONNECTING) {
// If we're still in the process of connecting, the server is not
// in a position to understand our close frame. Just nuke the
// connection and call it a day.
finishClose();
} else if (readyState === OPEN) {
sendClose();
if (timeout) {
setTimeout(finishClose, timeout * 1000);
}
}
};
self.send = function(str, fd) {
if (readyState != OPEN) {
return;
}
stream.write('\x00', 'binary');
stream.write(str, 'utf8', fd);
stream.write('\xff', 'binary');
};
// wss compat
self.write = self.send;
setConstant(self, 'url', url);
Object.defineProperty(self, 'readyState', {
get : function() {
return readyState;
}
});
// Connect and perform handshaking with the server
(function() {
// Parse constructor arguments
if (!url) {
throw new Error('Url and must be specified.');
}
// Secrets used for handshaking
var key1 = createSecretKey();
var key2 = createSecretKey();
var challenge = createChallenge();
debug(
'key1=\'' + str2hex(key1) + '\'; ' +
'key2=\'' + str2hex(key2) + '\'; ' +
'challenge=\'' + str2hex(challenge) + '\''
);
var httpHeaders = {
'Connection' : 'Upgrade',
'Upgrade' : 'WebSocket',
'Sec-WebSocket-Key1' : key1,
'Sec-WebSocket-Key2' : key2
};
if (opts.origin) {
httpHeaders['Origin'] = opts.origin;
}
if (proto) {
httpHeaders['Sec-WebSocket-Protocol'] = proto;
}
var httpPath = '/';
// Create the HTTP client that we'll use for handshaking. We'll cannabalize
// its socket via the 'upgrade' event and leave it to rot.
//
// N.B. The ws+unix:// scheme makes use of the implementation detail
// that http.Client passes its constructor arguments through,
// un-inspected to net.Stream.connect(). The latter accepts a
// string as its first argument to connect to a UNIX socket.
var opt = {};
var agent = null;
switch (getUrlScheme(url)) {
case 'ws':
var u = urllib.parse(url);
agent = new http.Agent({
host: u.hostname,
port: u.port || 80
});
opt.agent = agent;
opt.host = u.hostname;
opt.port = u.port || 80;
opt.path = (u.pathname || '/') + (u.search || '');
opt.headers = httpHeaders;
break;
case 'ws+unix':
var sockPath = url.substring('ws+unix://'.length, url.length);
var u = urllib.parse(url);
agent = new http.Agent({
host: 'localhost',
port: sockPath
});
opt.agent = agent;
opt.host = 'localhost';
opt.path = sockPath;
opt.headers = httpHeaders;
break;
default:
throw new Error('Invalid URL scheme \'' + urlScheme + '\' specified.');
}
var httpReq = http.request(opt, function() { });
var upgradeHandler = (function() {
var data = undefined;
return function(req, s, head) {
req.socket.setNoDelay(true);
stream = s;
if (readyState == CLOSED) {
stream.end();
stream.destroy();
stream = undefined;
return;
}
stream.on('data', function(d) {
if (d.length <= 0) {
return;
}
if (!data) {
data = d;
} else {
var data2 = new buffer.Buffer(data.length + d.length);
data.copy(data2, 0, 0, data.length);
d.copy(data2, data.length, 0, d.length);
data = data2;
}
if (data.length >= 16) {
var expected = computeSecretKeySignature(key1, key2, challenge);
var actual = data.slice(0, 16).toString('binary');
// Handshaking fails; we're donezo
if (actual != expected) {
debug(
'expected=\'' + str2hex(expected) + '\'; ' +
'actual=\'' + str2hex(actual) + '\''
);
process.nextTick(function() {
// N.B. Emit 'wserror' here, as 'error' is a reserved word in the
// EventEmitter world, and gets thrown.
self.emit(
'wserror',
new Error('Invalid handshake from server:' +
'expected \'' + str2hex(expected) + '\', ' +
'actual \'' + str2hex(actual) + '\''
)
);
if (self.onerror) {
self.onerror();
}
finishClose();
});
}
// Un-register our data handler and add the one to be used
// for the normal, non-handshaking case. If we have extra
// data left over, manually fire off the handler on
// whatever remains.
//
// XXX: This is lame. We should only remove the listeners
// that we added.
httpReq.removeAllListeners('upgrade');
stream.removeAllListeners('data');
stream.on('data', dataListener);
readyState = OPEN;
process.nextTick(function() {
self.emit('open');
if (self.onopen) {
self.onopen();
}
});
// Consume any leftover data
if (data.length > 16) {
stream.emit('data', data.slice(16, data.length));
}
}
});
stream.on('fd', fdListener);
stream.on('error', errorListener);
stream.on('close', function() {
errorListener(new Error('Stream closed unexpectedly.'));
});
stream.emit('data', head);
};
})();
agent.on('upgrade', upgradeHandler); // node v0.4
httpReq.on('upgrade', upgradeHandler); // node v0.5+
httpReq.write(challenge, 'binary');
httpReq.end();
})();
};
sys.inherits(WebSocket, events.EventEmitter);
exports.WebSocket = WebSocket;
// Add some constants to the WebSocket object
setConstant(WebSocket.prototype, 'CONNECTING', CONNECTING);
setConstant(WebSocket.prototype, 'OPEN', OPEN);
setConstant(WebSocket.prototype, 'CLOSING', CLOSING);
setConstant(WebSocket.prototype, 'CLOSED', CLOSED);
// vim:ts=4 sw=4 et

View file

@ -0,0 +1,22 @@
{
"name" : "websocket-client",
"version" : "1.0.0",
"description" : "An HTML5 Web Sockets client",
"author" : "Peter Griess <pg@std.in>",
"engines" : {
"node" : ">=0.1.98"
},
"repositories" : [
{
"type" : "git",
"url" : "http://github.com/pgriess/node-websocket-client.git"
}
],
"licenses" : [
{
"type" : "BSD",
"url" : "http://github.com/pgriess/node-websocket-client/blob/master/LICENSE"
}
],
"main" : "./lib/websocket"
}

View file

@ -0,0 +1,68 @@
// Verify that we can connect to a WebSocket server, exchange messages, and
// shut down cleanly.
var assert = require('assert');
var WebSocket = require('../lib/websocket').WebSocket;
var WebSocketServer = require('websocket-server/ws/server').Server;
var PORT = 1024 + Math.floor(Math.random() * 4096);
var C_MSG = 'Client test: ' + (Math.random() * 100);
var S_MSG = 'Server test: ' + (Math.random() * 100);
var serverGotConnection = false;
var clientGotOpen = false;
var clientGotData = false;
var clientGotMessage = false;
var serverGotMessage = false;
var serverGotClose = false;
var clientGotClose = false;
var wss = new WebSocketServer();
wss.listen(PORT, 'localhost');
wss.on('connection', function(c) {
serverGotConnection = true;
c.on('message', function(m) {
assert.equal(m, C_MSG);
serverGotMessage = true;
c.close();
});
c.on('close', function() {
serverGotClose = true;
wss.close();
});
c.write(S_MSG);
});
var ws = new WebSocket('ws://localhost:' + PORT + '/', 'biff');
ws.on('open', function() {
clientGotOpen = true;
});
ws.on('data', function(buf) {
assert.equal(typeof buf, 'object');
assert.equal(buf.toString('utf8'), S_MSG);
clientGotData = true;
ws.send(C_MSG);
});
ws.onmessage = function(m) {
assert.deepEqual(m, {data : S_MSG});
clientGotMessage = true;
};
ws.onclose = function() {
clientGotClose = true;
};
process.on('exit', function() {
assert.ok(serverGotConnection);
assert.ok(clientGotOpen);
assert.ok(clientGotData);
assert.ok(clientGotMessage);
assert.ok(serverGotMessage);
assert.ok(serverGotClose);
assert.ok(clientGotClose);
});

View file

@ -0,0 +1,43 @@
// Verify that a connection can be closed gracefully from the client.
var assert = require('assert');
var WebSocket = require('../lib/websocket').WebSocket;
var WebSocketServer = require('websocket-server/ws/server').Server;
var PORT = 1024 + Math.floor(Math.random() * 4096);
var C_MSG = 'Client test: ' + (Math.random() * 100);
var serverGotClientMessage = false;
var clientGotServerClose = false;
var serverGotClientClose = false;
var wss = new WebSocketServer();
wss.listen(PORT, 'localhost');
wss.on('connection', function(c) {
c.on('message', function(m) {
assert.equal(m, C_MSG);
serverGotClientMessage = true;
});
c.on('close', function() {
serverGotClientClose = true;
wss.close();
});
});
var ws = new WebSocket('ws://localhost:' + PORT);
ws.onopen = function() {
ws.send(C_MSG);
// XXX: Add a timeout here
ws.close(5);
};
ws.onclose = function() {
assert.equal(ws.CLOSED, ws.readyState);
clientGotServerClose = true;
};
process.on('exit', function() {
assert.ok(serverGotClientMessage);
assert.ok(clientGotServerClose);
assert.ok(serverGotClientClose);
});

View file

@ -0,0 +1,43 @@
// Verify that some attributes of a WebSocket object are read-only.
var assert = require('assert');
var sys = require('sys');
var WebSocket = require('../lib/websocket').WebSocket;
var WebSocketServer = require('websocket-server/ws/server').Server;
var PORT = 1024 + Math.floor(Math.random() * 4096);
var wss = new WebSocketServer();
wss.listen(PORT, 'localhost');
wss.on('connection', function(c) {
c.close();
wss.close();
});
var ws = new WebSocket('ws://localhost:' + PORT + '/', 'biff');
ws.on('open', function() {
assert.equal(ws.CONNECTING, 0);
try {
ws.CONNECTING = 13;
assert.equal(
ws.CONNECTING, 0,
'Should not have been able to set read-only CONNECTING attribute'
);
} catch (e) {
assert.equal(e.type, 'no_setter_in_callback');
}
assert.equal(ws.OPEN, 1);
assert.equal(ws.CLOSING, 2);
assert.equal(ws.CLOSED, 3);
assert.equal(ws.url, 'ws://localhost:' + PORT + '/');
try {
ws.url = 'foobar';
assert.equal(
ws.url, 'ws://localhost:' + PORT + '/',
'Should not have been able to set read-only url attribute'
);
} catch (e) {
assert.equal(e.type, 'no_setter_in_callback');
}
});

View file

@ -0,0 +1,26 @@
// Verify that readyState transitions are implemented correctly
var assert = require('assert');
var WebSocket = require('../lib/websocket').WebSocket;
var WebSocketServer = require('websocket-server/ws/server').Server;
var PORT = 1024 + Math.floor(Math.random() * 4096);
var wss = new WebSocketServer();
wss.listen(PORT, 'localhost');
wss.on('connection', function(c) {
c.close();
});
var ws = new WebSocket('ws://localhost:' + PORT);
assert.equal(ws.readyState, ws.CONNECTING);
ws.onopen = function() {
assert.equal(ws.readyState, ws.OPEN);
ws.close();
assert.ok(ws.readyState == ws.CLOSING);
};
ws.onclose = function() {
assert.equal(ws.readyState, ws.CLOSED);
wss.close();
};

View file

@ -0,0 +1,41 @@
// Verify that a connection can be closed gracefully from the server.
var assert = require('assert');
var WebSocket = require('../lib/websocket').WebSocket;
var WebSocketServer = require('websocket-server/ws/server').Server;
var PORT = 1024 + Math.floor(Math.random() * 4096);
var S_MSG = 'Server test: ' + (Math.random() * 100);
var clientGotServerMessage = false;
var clientGotServerClose = false;
var serverGotClientClose = false;
var wss = new WebSocketServer();
wss.listen(PORT, 'localhost');
wss.on('connection', function(c) {
c.on('close', function() {
serverGotClientClose = true;
wss.close();
});
c.write(S_MSG);
c.close();
});
var ws = new WebSocket('ws://localhost:' + PORT);
ws.onmessage = function(m) {
assert.deepEqual(m, {data: S_MSG});
clientGotServerMessage = true;
};
ws.onclose = function() {
assert.equal(ws.CLOSED, ws.readyState);
clientGotServerClose = true;
};
process.on('exit', function() {
assert.ok(clientGotServerMessage);
assert.ok(clientGotServerClose);
assert.ok(serverGotClientClose);
});

View file

@ -0,0 +1,63 @@
// Verify that both sides of the WS connection can both send and receive file
// descriptors.
var assert = require('assert');
var fs = require('fs');
var path = require('path');
var sys = require('sys');
var WebSocket = require('../lib/websocket').WebSocket;
var WebSocketServer = require('websocket-server/ws/server').Server;
var PATH = path.join(__dirname, 'sock.' + process.pid);
var C_MSG = 'Client test: ' + (Math.random() * 100);
var S_MSG = 'Server test: ' + (Math.random() * 100);
var clientReceivedData = false;
var clientReceivedFD = false;
var serverReceivedData = false;
var serverReceivedFD = false;
var wss = new WebSocketServer();
wss.on('listening', function() {
var ws = new WebSocket('ws+unix://' + PATH);
ws.on('data', function(d) {
assert.equal(d.toString('utf8'), S_MSG);
clientReceivedData = true;
ws.send(C_MSG, 1);
ws.close();
});
ws.on('fd', function(fd) {
assert.ok(fd >= 0);
clientReceivedFD = true;
});
});
wss.on('connection', function(c) {
c.write(S_MSG, 0);
c._req.socket.on('fd', function(fd) {
assert.ok(fd >= 0);
serverReceivedFD = true;
});
c.on('message', function(d) {
assert.equal(d, C_MSG);
serverReceivedData = true;
wss.close();
});
});
wss.listen(PATH);
process.on('exit', function() {
assert.ok(clientReceivedFD);
assert.ok(clientReceivedData);
assert.ok(serverReceivedFD);
assert.ok(serverReceivedData);
try {
fs.unlinkSync(PATH);
} catch (e) { }
});

View file

@ -0,0 +1,46 @@
// Verify that we can connect to a server over UNIX domain sockets.
var assert = require('assert');
var fs = require('fs');
var path = require('path');
var sys = require('sys');
var WebSocket = require('../lib/websocket').WebSocket;
var WebSocketServer = require('websocket-server/ws/server').Server;
var PATH = path.join(__dirname, 'sock.' + process.pid);
var S_MSG = 'Server test: ' + (Math.random() * 100);
var serverGotConnection = false;
var clientGotOpen = false;
var clientGotData = false;
var wss = new WebSocketServer();
wss.on('listening', function() {
var ws = new WebSocket('ws+unix://' + PATH);
ws.on('open', function() {
clientGotOpen = true;
ws.close();
});
ws.on('data', function(d) {
assert.equal(d.toString('utf8'), S_MSG);
clientGotData = true;
});
});
wss.on('connection', function(c) {
serverGotConnection = true;
c.write(S_MSG);
wss.close();
});
wss.listen(PATH);
process.on('exit', function() {
assert.ok(serverGotConnection);
assert.ok(clientGotOpen);
assert.ok(clientGotData);
try {
fs.unlinkSync(PATH);
} catch(e) { }
});