Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial import

  • Loading branch information...
commit 19093dc604fe52c498560d7fc19570074714e459 0 parents
@einaros einaros authored
5 .gitignore
@@ -0,0 +1,5 @@
+npm-debug.log
+node_modules
+history/
+cache/
+.*.swp
13 Makefile
@@ -0,0 +1,13 @@
+ALL_TESTS = $(shell find test/ -name '*.test.js')
+
+run-tests:
+ @./node_modules/.bin/expresso \
+ -t 2000 \
+ --serial \
+ $(TESTFLAGS) \
+ $(TESTS)
+
+test:
+ @$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests
+
+.PHONY: test
0  README.md
No changes.
1  index.js
@@ -0,0 +1 @@
+module.exports = require('./lib/Wetsock');
68 lib/Wetsock.js
@@ -0,0 +1,68 @@
+var util = require('util');
+var events = require('events');
+var http = require('http');
+var crypto = require('crypto');
+
+function Wetsock(server, port, options) {
+ var serverPort = 80;
+ if (typeof port === 'number') serverPort = port;
+ else if (typeof port === 'object') options = port;
+ options = options || {};
+ options.origin = options.origin || null;
+
+ var key = 'dGhlIHNhbXBsZSBub25jZQ==';
+ var shasum = crypto.createHash('sha1');
+ shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ var expectedServerKey = shasum.digest('base64');
+
+ var requestOptions = {
+ port: serverPort,
+ host: server,
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Version': 13,
+ 'Sec-WebSocket-Key': key
+ }
+ };
+ if (options.origin) requestOptions.headers.origin = options.origin;
+
+ var req = http.request(requestOptions);
+ req.end();
+ this._socket = null;
+ this._state = 'connecting';
+ var self = this;
+ req.on('upgrade', function(res, socket, upgradeHead) {
+ if (self._state == 'disconnected') {
+ socket.end();
+ return;
+ }
+ var serverKey = res.headers['sec-websocket-accept'];
+ if (typeof serverKey == 'undefined' || serverKey !== expectedServerKey) {
+ self.emit('error', 'invalid server key');
+ socket.end();
+ return;
+ }
+ self._socket = socket;
+ self._state = 'connected';
+ self.emit('connect');
+ });
+}
+util.inherits(Wetsock, events.EventEmitter);
+
+Wetsock.prototype.close = function() {
+ if (this._socket) {
+ this._socket.end();
+ this._socket = null;
+ }
+ else if (this._state == 'connecting') {
+ this._state = 'disconnected';
+ }
+}
+
+Wetsock.prototype.send = function(data, options) {
+ if (this._state != 'connected') throw 'not connected';
+
+}
+
+module.exports = Wetsock;
20 package.json
@@ -0,0 +1,20 @@
+{
+ "author": "Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)",
+ "name": "wetsock",
+ "description": "microscopic websocket client",
+ "version": "0.0.1",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/einaros/wetsock.git"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "engines": {
+ "node": "~0.6.0"
+ },
+ "dependencies": {
+ "expresso": "latest"
+ },
+ "devDependencies": {}
+}
42 test/Wetsock.test.js
@@ -0,0 +1,42 @@
+var assert = require('assert');
+var Wetsock = require('../');
+var server = require('./server');
+
+var port = 20000;
+
+module.exports = {
+ 'can connect to echo service': function() {
+ var ws = new Wetsock('echo.websocket.org');
+ ws.on('connect', function() {
+ ws.close();
+ });
+ },
+ 'can disconnect before connection is established': function(done) {
+ var ws = new Wetsock('echo.websocket.org');
+ ws.close();
+ ws.on('connect', function() {
+ assert.fail('connect shouldnt be raised here');
+ });
+ ws.on('disconnect', function() {
+ done();
+ });
+ },
+ 'send before connect should fail': function(done) {
+ var ws = new Wetsock('echo.websocket.org');
+ try {
+ ws.send('hi');
+ }
+ catch (e) {
+ ws.close();
+ done();
+ }
+ },
+ 'invalid server key is denied': function(done) {
+ var srv = server.listen(++port, server.handlers.invalidKey);
+ var ws = new Wetsock('localhost', port);
+ ws.on('error', function() {
+ srv.close();
+ done();
+ });
+ },
+}
82 test/server.js
@@ -0,0 +1,82 @@
+var http = require('http');
+var crypto = require('crypto');
+
+function validRequestHandler(req, socket) {
+ if (typeof req.headers.upgrade === 'undefined' ||
+ req.headers.upgrade.toLowerCase() !== 'websocket') {
+ throw 'invalid headers';
+ return;
+ }
+
+ if (!req.headers['sec-websocket-key']) {
+ socket.end();
+ throw 'websocket key is missing';
+ }
+
+ // calc key
+ var key = req.headers['sec-websocket-key'];
+ var shasum = crypto.createHash('sha1');
+ shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ key = shasum.digest('base64');
+
+ var headers = [
+ 'HTTP/1.1 101 Switching Protocols'
+ , 'Upgrade: websocket'
+ , 'Connection: Upgrade'
+ , 'Sec-WebSocket-Accept: ' + key
+ ];
+
+ socket.write(headers.concat('', '').join('\r\n'));
+ socket.end();
+ // socket.on('data', function (data) {
+ // self.parser.add(data);
+ // });
+}
+
+function invalidRequestHandler(req, socket) {
+ if (typeof req.headers.upgrade === 'undefined' ||
+ req.headers.upgrade.toLowerCase() !== 'websocket') {
+ throw 'invalid headers';
+ return;
+ }
+
+ if (!req.headers['sec-websocket-key']) {
+ socket.end();
+ throw 'websocket key is missing';
+ }
+
+ // calc key
+ var key = req.headers['sec-websocket-key'];
+ var shasum = crypto.createHash('sha1');
+ shasum.update(key + "bogus");
+ key = shasum.digest('base64');
+
+ var headers = [
+ 'HTTP/1.1 101 Switching Protocols'
+ , 'Upgrade: websocket'
+ , 'Connection: Upgrade'
+ , 'Sec-WebSocket-Accept: ' + key
+ ];
+
+ socket.write(headers.concat('', '').join('\r\n'));
+ socket.end();
+ // socket.on('data', function (data) {
+ // self.parser.add(data);
+ // });
+}
+
+module.exports = {
+ handlers: {
+ valid: validRequestHandler,
+ invalidKey: invalidRequestHandler,
+ },
+ listen: function(port, handler) {
+ var srv = http.createServer(function (req, res) {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end('okay');
+ });
+ srv.on('upgrade', handler || validRequestHandler);
+ srv.listen(port, '127.0.0.1');
+ return srv;
+ }
+};
Please sign in to comment.
Something went wrong with that request. Please try again.