diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..09d3ef3
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+ - 0.8
+ - 0.10
diff --git a/README.md b/README.md
index 9fc4515..8568436 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-# PeerJS Server: Server component for PeerJS #
+[](https://travis-ci.org/peers/peerjs-server)
+
+# PeerServer: A server for PeerJS #
PeerServer helps broker connections between PeerJS clients. Data is not proxied through the server.
@@ -12,32 +14,46 @@ PeerServer helps broker connections between PeerJS clients. Data is not proxied
Install the library:
- npm install peer
+```bash
+$> npm install peer
+```
Run the server:
- var PeerServer = require('peer').PeerServer;
- var server = new PeerServer({ port: 9000 });
+```bash
+$> peerjs --port 9000 --key peerjs
+```
+
+Or, create a custom server:
+
+```javascript
+var PeerServer = require('peer').PeerServer;
+var server = new PeerServer({ port: 9000 });
+```
Connecting to the server from PeerJS:
-
+```html
+
+```
Using HTTPS: Simply pass in PEM-encoded certificate and key.
- var fs = require('fs');
- var PeerServer = require('peer').PeerServer;
-
- var server = new PeerServer({
- port: 9000,
- ssl: {
- key: fs.readFileSync('/path/to/your/ssl/key/here.key'),
- certificate: fs.readFileSync('/path/to/your/ssl/certificate/here.crt')
- }
- });
+```javascript
+var fs = require('fs');
+var PeerServer = require('peer').PeerServer;
+
+var server = new PeerServer({
+ port: 9000,
+ ssl: {
+ key: fs.readFileSync('/path/to/your/ssl/key/here.key'),
+ certificate: fs.readFileSync('/path/to/your/ssl/certificate/here.crt')
+ }
+});
+```
## Problems?
diff --git a/bin/peerjs b/bin/peerjs
new file mode 100755
index 0000000..83ba95a
--- /dev/null
+++ b/bin/peerjs
@@ -0,0 +1,71 @@
+#!/usr/bin/env node
+
+var path = require('path')
+ , pkg = require('../package.json')
+ , fs = require('fs')
+ , version = pkg.version
+ , PeerServer = require('../lib/server').PeerServer
+ , opts = require('optimist')
+ .usage('Usage: $0')
+ .options({
+ debug: {
+ demand: false,
+ alias: 'd',
+ description: 'debug',
+ default: false
+ },
+ timeout: {
+ demand: false,
+ alias: 't',
+ description: 'timeout (milliseconds)',
+ default: 5000
+ },
+ ip_limit: {
+ demand: false,
+ alias: 'i',
+ description: 'IP limit',
+ default: 5000
+ },
+ concurrent_limit: {
+ demand: false,
+ alias: 'c',
+ description: 'concurrent limit',
+ default: 5000
+ },
+ key: {
+ demand: false,
+ alias: 'k',
+ description: 'connection key',
+ default: 'peerjs'
+ },
+ sslkey: {
+ demand: false,
+ description: 'path to SSL key'
+ },
+ sslcert: {
+ demand: false,
+ description: 'path to SSL certificate'
+ },
+ port: {
+ demand: true,
+ alias: 'p',
+ description: 'port',
+ }
+ }).argv;
+
+opts.version = version;
+
+if (opts.sslkey && opts.sslcert) {
+ opts['ssl'] = {};
+ opts.ssl['key'] = fs.readFileSync(path.resolve(opts.sslkey));
+ opts.ssl['certificate'] = fs.readFileSync(path.resolve(opts.sslcert));
+}
+
+process.on('uncaughtException', function(e) {
+ console.error('Error: ' + e);
+});
+
+var server = new PeerServer(opts);
+console.log(
+ "Started PeerServer, port: " + opts.port + (" (v. %s)"), version
+);
diff --git a/lib/server.js b/lib/server.js
index 5cf2572..0e5d087 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -21,8 +21,6 @@ function PeerServer(options) {
util.debug = this._options.debug;
- // Set up HTTPS server if key and certificate are provided.
- var secure = this._options.ssl.key && this._options.ssl.certificate;
// Print warning if only one of the two is given.
if (Object.keys(this._options.ssl).length === 1) {
util.prettyError('Warning: PeerServer will not run on an HTTPS server'
@@ -49,7 +47,7 @@ function PeerServer(options) {
this._ips = {};
this._setCleanupIntervals();
-};
+}
util.inherits(PeerServer, EventEmitter);
@@ -73,7 +71,7 @@ PeerServer.prototype._initializeWSS = function() {
socket.close();
return;
}
-
+
if (!self._clients[key] || !self._clients[key][id]) {
self._checkKey(key, ip, function(err) {
if (!err) {
@@ -126,35 +124,22 @@ PeerServer.prototype._configureWS = function(socket, key, id, token) {
try {
var message = JSON.parse(data);
- switch (message.type) {
- case 'LEAVE':
- // Clean up if a Peer sends a LEAVE.
- if (!message.dst) {
- self._removePeer(key, id);
- break;
- }
- // ICE candidates
- case 'CANDIDATE':
- // Offer or answer between peers.
- case 'OFFER':
- case 'ANSWER':
- // Use the ID we know to be correct to prevent spoofing.
- self._handleTransmission(key, {
- type: message.type,
- src: id,
- dst: message.dst,
- payload: message.payload
- });
- break;
- default:
- util.prettyError('Message unrecognized');
+ if (['LEAVE', 'CANDIDATE', 'OFFER', 'ANSWER'].indexOf(message.type) !== -1) {
+ self._handleTransmission(key, {
+ type: message.type,
+ src: id,
+ dst: message.dst,
+ payload: message.payload
+ });
+ } else {
+ util.prettyError('Message unrecognized');
}
} catch(e) {
- throw e;
util.log('Invalid message', data);
+ throw e;
}
});
-}
+};
PeerServer.prototype._checkKey = function(key, ip, cb) {
@@ -181,14 +166,14 @@ PeerServer.prototype._checkKey = function(key, ip, cb) {
} else {
cb('Invalid key provided');
}
-}
+};
/** Initialize HTTP server routes. */
PeerServer.prototype._initializeHTTP = function() {
var self = this;
this._app.use(restify.bodyParser({ mapParams: false }));
- this._app.use(restify.queryParser())
+ this._app.use(restify.queryParser());
this._app.use(util.allowCrossDomain);
// Retrieve guaranteed random ID.
@@ -203,7 +188,7 @@ PeerServer.prototype._initializeHTTP = function() {
var id = req.params.id;
var token = req.params.token;
var key = req.params.key;
- var ip = req.ip;
+ var ip = req.connection.remoteAddress;
if (!self._clients[key] || !self._clients[key][id]) {
self._checkKey(key, ip, function(err) {
@@ -307,7 +292,7 @@ PeerServer.prototype._pruneOutstanding = function() {
var keys = Object.keys(this._outstanding);
for (var k = 0, kk = keys.length; k < kk; k += 1) {
var key = keys[k];
- var dsts = Object.keys(this._outstanding[key]);
+ var dsts = Object.keys(this._outstanding[key]);
for (var i = 0, ii = dsts.length; i < ii; i += 1) {
var offers = this._outstanding[key][dsts[i]];
var seen = {};
@@ -332,7 +317,7 @@ PeerServer.prototype._setCleanupIntervals = function() {
var keys = Object.keys(self._ips);
for (var i = 0, ii = keys.length; i < ii; i += 1) {
var key = keys[i];
- if (self._ips[key] == 0) {
+ if (self._ips[key] === 0) {
delete self._ips[key];
}
}
@@ -383,7 +368,7 @@ PeerServer.prototype._handleTransmission = function(key, message) {
destination.res.write(data);
} else {
// Neither socket no res available. Peer dead?
- throw "Peer dead"
+ throw "Peer dead";
}
} catch (e) {
// This happens when a peer disconnects without closing connections and
diff --git a/lib/util.js b/lib/util.js
index 01b0e89..8a81cf1 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -13,6 +13,7 @@ var util = {
});
},
extend: function(dest, source) {
+ source = source || {};
for(var key in source) {
if(source.hasOwnProperty(key)) {
dest[key] = source[key];
diff --git a/package.json b/package.json
index 2da52db..a4240f6 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,9 @@
{
"name": "peer",
- "version": "0.1.6",
+ "version": "0.2.0",
"description": "Peer-to-peer data in browsers",
"main": "lib/server.js",
+ "bin": { "peerjs": "./bin/peerjs" },
"repository": {
"type": "git",
"url": "git://github.com/peers/peerjs-server.git"
@@ -11,6 +12,18 @@
"license": "MIT",
"dependencies": {
"restify": "~2.3.5",
- "ws": "~0.4.25"
+ "ws": "~0.4.25",
+ "optimist": "*"
+ },
+ "devDependencies": {
+ "expect.js": "*",
+ "sinon": "*",
+ "mocha": "*"
+ },
+ "engines": {
+ "node": ">=0.8"
+ },
+ "scripts": {
+ "test": "mocha test"
}
}
diff --git a/test/server.js b/test/server.js
new file mode 100644
index 0000000..8a04bc5
--- /dev/null
+++ b/test/server.js
@@ -0,0 +1,113 @@
+var PeerServer = require('../').PeerServer;
+var expect = require('expect.js');
+var sinon = require('sinon');
+
+describe('PeerServer', function() {
+ describe('constructor', function() {
+ before(function() {
+ PeerServer.prototype._initializeWSS = sinon.stub();
+ PeerServer.prototype._initializeHTTP = sinon.stub();
+ });
+
+ it('should be able to be created without the `new` keyword', function() {
+ var p = PeerServer();
+ expect(p.constructor).to.be(PeerServer);
+ });
+
+ it('should default to port 80, key `peerjs`', function() {
+ var p = new PeerServer();
+ expect(p._options.key).to.be('peerjs');
+ expect(p._options.port).to.be(80);
+ });
+
+ it('should accept a custom port', function() {
+ var p = new PeerServer({ port: 8000 });
+ expect(p._options.port).to.be(8000);
+ });
+ });
+
+ describe('#_initializeWSS', function() {
+ WebSocketServer = sinon.stub();
+
+ });
+
+ describe('#_configureWS', function() {
+
+ });
+
+ describe('#_checkKey', function() {
+ var p;
+ before(function() {
+ PeerServer.prototype._initializeHTTP = sinon.stub();
+ p = new PeerServer({ port: 8000 });
+ p._checkKey('peerjs', 'myip', function() {});
+ });
+
+ it('should reject keys that are not the default', function(done) {
+ p._checkKey('bad key', null, function(response) {
+ expect(response).to.be('Invalid key provided');
+ done();
+ });
+ });
+
+ it('should accept valid key/ip pairs', function(done) {
+ p._checkKey('peerjs', 'myip', function(response) {
+ expect(response).to.be(null);
+ done();
+ });
+ });
+
+ it('should reject ips that are at their limit', function(done) {
+ p._options.ip_limit = 0;
+ p._checkKey('peerjs', 'myip', function(response) {
+ expect(response).to.be('myip has reached its concurrent user limit');
+ done();
+ });
+ });
+
+ it('should reject when the server is at its limit', function(done) {
+ p._options.concurrent_limit = 0;
+ p._checkKey('peerjs', 'myip', function(response) {
+ expect(response).to.be('Server has reached its concurrent user limit');
+ done();
+ });
+ });
+
+ });
+
+ describe('#_initializeHTTP', function() {
+
+ });
+
+ describe('#_startStreaming', function() {
+
+ });
+
+ describe('#_pruneOutstanding', function() {
+
+ });
+
+ describe('#_processOutstanding', function() {
+
+ });
+
+ describe('#_removePeer', function() {
+
+ });
+
+ describe('#_handleTransmission', function() {
+ // TODO: this is probably the most important method to test.
+ });
+
+ describe('#_generateClientId', function() {
+ var p;
+ before(function() {
+ PeerServer.prototype._initializeHTTP = sinon.stub();
+ p = new PeerServer({ port: 8000 });
+ });
+
+ it('should generate a 16-character ID', function() {
+ expect(p._generateClientId('anykey').length).to.be(16);
+ });
+ });
+});