2014-04-08 14:35:43 -07:00
|
|
|
|
|
|
|
var imports = require('soop').imports();
|
|
|
|
var EventEmitter= imports.EventEmitter || require('events').EventEmitter;
|
|
|
|
|
2014-04-08 22:20:28 -07:00
|
|
|
/*
|
|
|
|
* Emits
|
|
|
|
* 'networkChange'
|
|
|
|
* when network layout has change (new/lost peers, etc)
|
|
|
|
*
|
|
|
|
* 'data'
|
|
|
|
* when an unknown data type arrives
|
|
|
|
*
|
|
|
|
* Provides
|
|
|
|
* send(toPeerIds, {data}, cb?)
|
|
|
|
*
|
|
|
|
*/
|
2014-04-08 14:35:43 -07:00
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
function Network(opts) {
|
2014-04-09 07:05:25 -07:00
|
|
|
opts = opts || {};
|
|
|
|
this.peerId = opts.peerId;
|
|
|
|
this.apiKey = opts.apiKey || 'lwjd5qra8257b9';
|
|
|
|
this.debug = opts.debug || 3;
|
|
|
|
this.maxPeers = opts.maxPeers || 5;
|
2014-04-08 14:35:43 -07:00
|
|
|
this.connectedPeers = [];
|
|
|
|
}
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.parent=EventEmitter;
|
2014-04-08 14:35:43 -07:00
|
|
|
|
|
|
|
// Array helpers
|
2014-04-14 11:31:10 -07:00
|
|
|
Network._arrayDiff = function(a, b) {
|
2014-04-08 14:35:43 -07:00
|
|
|
var seen = [];
|
|
|
|
var diff = [];
|
|
|
|
|
|
|
|
for (var i = 0; i < b.length; i++)
|
|
|
|
seen[b[i]] = true;
|
|
|
|
|
|
|
|
for (var j = 0; j < a.length; j++)
|
|
|
|
if (!seen[a[j]])
|
|
|
|
diff.push(a[j]);
|
|
|
|
|
|
|
|
return diff;
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network._inArray = function(el, array) {
|
2014-04-08 14:35:43 -07:00
|
|
|
return array.indexOf(el) > -1;
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network._arrayPushOnce = function(el, array) {
|
2014-04-08 14:35:43 -07:00
|
|
|
var ret = false;
|
2014-04-14 11:31:10 -07:00
|
|
|
if (!Network._inArray(el, array)) {
|
2014-04-08 14:35:43 -07:00
|
|
|
array.push(el);
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network._arrayRemove = function(el, array) {
|
2014-04-08 14:35:43 -07:00
|
|
|
var pos = array.indexOf(el);
|
|
|
|
if (pos >= 0) array.splice(pos, 1);
|
|
|
|
|
|
|
|
return array;
|
|
|
|
};
|
|
|
|
|
|
|
|
// DEBUG
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype._showConnectedPeers = function() {
|
2014-04-08 14:35:43 -07:00
|
|
|
console.log("### CONNECTED PEERS", this.connectedPeers);
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype._onClose = function(peerId) {
|
|
|
|
console.log('[Network.js.70] _onClose'); //TODO
|
|
|
|
this.connectedPeers = Network._arrayRemove(peerId, this.connectedPeers);
|
2014-04-08 14:35:43 -07:00
|
|
|
this._notify();
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype._connectToPeers = function(peerIds) {
|
2014-04-08 14:35:43 -07:00
|
|
|
var self = this;
|
|
|
|
var ret = false;
|
2014-04-14 11:31:10 -07:00
|
|
|
var arrayDiff1= Network._arrayDiff(peerIds, this.connectedPeers);
|
|
|
|
var arrayDiff = Network._arrayDiff(arrayDiff1, [this.peerId]);
|
2014-04-08 14:35:43 -07:00
|
|
|
arrayDiff.forEach(function(peerId) {
|
|
|
|
console.log('### CONNECTING TO:', peerId);
|
|
|
|
self.connectTo(peerId);
|
|
|
|
ret = true;
|
|
|
|
});
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype._onData = function(data, isInbound) {
|
2014-04-09 07:05:25 -07:00
|
|
|
var obj;
|
|
|
|
try {
|
|
|
|
obj = JSON.parse(data);
|
|
|
|
} catch (e) {
|
|
|
|
console.log('### ERROR ON DATA: "%s" ', data, isInbound, e);
|
|
|
|
return;
|
|
|
|
};
|
2014-04-12 09:37:41 -07:00
|
|
|
console.log('### RECEIVED TYPE: %s FROM %s', obj.data.type, obj.sender, obj.data);
|
2014-04-08 14:35:43 -07:00
|
|
|
|
|
|
|
switch(obj.data.type) {
|
|
|
|
case 'peerList':
|
2014-04-08 22:20:28 -07:00
|
|
|
this._connectToPeers(obj.data.peers);
|
2014-04-08 14:35:43 -07:00
|
|
|
this._notify();
|
|
|
|
break;
|
|
|
|
case 'disconnect':
|
|
|
|
this._onClose(obj.sender);
|
2014-04-08 22:20:28 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
this.emit('data', obj.sender, obj.data, isInbound);
|
2014-04-08 14:35:43 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype._sendPeers = function(peerIds) {
|
2014-04-09 07:05:25 -07:00
|
|
|
console.log('#### SENDING PEER LIST: ', this.connectedPeers, ' TO ', peerIds?peerIds: 'ALL');
|
2014-04-08 14:35:43 -07:00
|
|
|
this.send(peerIds, {
|
|
|
|
type: 'peerList',
|
|
|
|
peers: this.connectedPeers,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype._addPeer = function(peerId, isInbound) {
|
2014-04-08 14:35:43 -07:00
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
var hasChanged = Network._arrayPushOnce(peerId, this.connectedPeers);
|
2014-04-08 14:35:43 -07:00
|
|
|
|
|
|
|
|
|
|
|
if (isInbound && hasChanged) {
|
|
|
|
this._sendPeers(); //broadcast peer list
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (isInbound) {
|
|
|
|
this._sendPeers(peerId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype._setupConnectionHandlers = function(
|
2014-04-09 07:05:25 -07:00
|
|
|
dataConn, isInbound, openCallback, closeCallback) {
|
|
|
|
|
2014-04-08 14:35:43 -07:00
|
|
|
var self=this;
|
|
|
|
|
|
|
|
dataConn.on('open', function() {
|
2014-04-14 11:31:10 -07:00
|
|
|
if (!Network._inArray(dataConn.peer, self.connectedPeers)) {
|
2014-04-08 14:35:43 -07:00
|
|
|
|
|
|
|
console.log('### DATA CONNECTION READY TO: ADDING PEER: %s (inbound: %s)',
|
|
|
|
dataConn.peer, isInbound);
|
|
|
|
|
|
|
|
self._addPeer(dataConn.peer, isInbound);
|
2014-04-08 22:20:28 -07:00
|
|
|
self._notify( isInbound ? dataConn.peer : null);
|
2014-04-08 14:35:43 -07:00
|
|
|
if (typeof openCallback === 'function') openCallback();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
dataConn.on('data', function(data) {
|
|
|
|
self._onData(data, isInbound);
|
|
|
|
});
|
|
|
|
|
|
|
|
dataConn.on('error', function(e) {
|
2014-04-09 07:05:25 -07:00
|
|
|
console.log('### DATA ERROR',e ); //TODO
|
2014-04-08 14:35:43 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
dataConn.on('close', function() {
|
2014-04-12 09:37:41 -07:00
|
|
|
if (self.closing) return;
|
|
|
|
console.log('### CLOSE RECV FROM:', dataConn.peer);
|
|
|
|
|
2014-04-08 14:35:43 -07:00
|
|
|
self._onClose(dataConn.peer);
|
2014-04-09 07:05:25 -07:00
|
|
|
if (typeof closeCallback === 'function') closeCallback();
|
2014-04-08 14:35:43 -07:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype._notify = function(newPeer) {
|
|
|
|
console.log('[Network.js.168:_notify:]'); //TODO
|
2014-04-08 14:35:43 -07:00
|
|
|
this._showConnectedPeers();
|
2014-04-08 22:20:28 -07:00
|
|
|
this.emit('networkChange', newPeer);
|
2014-04-08 14:35:43 -07:00
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype._setupPeerHandlers = function(openCallback) {
|
2014-04-08 14:35:43 -07:00
|
|
|
var self=this;
|
|
|
|
var p = this.peer;
|
|
|
|
|
|
|
|
|
|
|
|
p.on('open', function(peerId) {
|
|
|
|
console.log('### PEER OPEN. I AM:' + peerId);
|
|
|
|
self.peerId = peerId;
|
|
|
|
self.connectedPeers = [peerId];
|
|
|
|
self._notify();
|
|
|
|
return openCallback(peerId);
|
|
|
|
});
|
|
|
|
|
|
|
|
p.on('error', function(err) {
|
|
|
|
console.log('### PEER ERROR:', err);
|
2014-04-12 09:37:41 -07:00
|
|
|
self.peer.disconnect();
|
|
|
|
self.peer.destroy();
|
|
|
|
self.peer = null;
|
|
|
|
this.emit('abort');
|
2014-04-08 14:35:43 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
p.on('connection', function(dataConn) {
|
2014-04-09 07:05:25 -07:00
|
|
|
|
|
|
|
console.log('### NEW INBOUND CONNECTION %d/%d', self.connectedPeers.length, self.maxPeers);
|
|
|
|
if (self.connectedPeers.length >= self.maxPeers) {
|
|
|
|
console.log('### PEER REJECTED. PEER MAX LIMIT REACHED');
|
|
|
|
dataConn.on('open', function() {
|
|
|
|
console.log('### CLOSING CONN FROM:' + dataConn.peer);
|
|
|
|
dataConn.close();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self._setupConnectionHandlers(dataConn, true);
|
|
|
|
}
|
2014-04-08 14:35:43 -07:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype.start = function(openCallback) {
|
2014-04-08 14:35:43 -07:00
|
|
|
// Start PeerJS Peer
|
|
|
|
this.peer = new Peer(this.peerId, {
|
|
|
|
key: this.apiKey, // TODO: we need our own PeerServer KEY (http://peerjs.com/peerserver)
|
|
|
|
debug: this.debug,
|
|
|
|
});
|
|
|
|
|
|
|
|
this._setupPeerHandlers(openCallback);
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype._sendToOne = function(peerId, data, cb) {
|
2014-04-08 14:35:43 -07:00
|
|
|
if (peerId !== this.peerId) {
|
2014-04-15 08:52:28 -07:00
|
|
|
console.log('[WebRTC.js.222:peerId:]',peerId, data); //TODO
|
|
|
|
|
2014-04-08 14:35:43 -07:00
|
|
|
var conns = this.peer.connections[peerId];
|
|
|
|
|
|
|
|
if (conns) {
|
|
|
|
var str = JSON.stringify({
|
|
|
|
sender: this.peerId,
|
|
|
|
data: data
|
|
|
|
});
|
|
|
|
|
|
|
|
for (var i = 0; i < conns.length; i++) {
|
|
|
|
var conn = conns[i];
|
|
|
|
conn.send(str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (typeof cb === 'function') cb();
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype.send = function(peerIds, data, cb) {
|
2014-04-08 14:35:43 -07:00
|
|
|
var self=this;
|
|
|
|
|
2014-04-15 08:52:28 -07:00
|
|
|
console.log('[WebRTC.js.242:peerIds:]',peerIds); //TODO
|
2014-04-08 22:20:28 -07:00
|
|
|
if (!peerIds) {
|
|
|
|
peerIds = this.connectedPeers;
|
|
|
|
data.isBroadcast = 1;
|
|
|
|
}
|
2014-04-15 08:52:28 -07:00
|
|
|
console.log('[WebRTC.js.246:peerIds:]',peerIds, data); //TODO
|
2014-04-08 22:20:28 -07:00
|
|
|
|
2014-04-08 14:35:43 -07:00
|
|
|
if (Array.isArray(peerIds)) {
|
|
|
|
var l = peerIds.length;
|
|
|
|
var i = 0;
|
|
|
|
peerIds.forEach(function(peerId) {
|
|
|
|
self._sendToOne(peerId, data, function () {
|
|
|
|
if (++i === l && typeof cb === 'function') cb();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else if (typeof peerIds === 'string')
|
|
|
|
self._sendToOne(peerIds, data, cb);
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype.connectTo = function(peerId, openCallback, closeCallback ) {
|
2014-04-08 14:35:43 -07:00
|
|
|
var self = this;
|
|
|
|
|
|
|
|
console.log('### STARTING TO CONNECT TO:' + peerId );
|
|
|
|
|
|
|
|
var dataConn = this.peer.connect(peerId, {
|
|
|
|
// label: 'wallet',
|
|
|
|
serialization: 'none',
|
|
|
|
reliable: true,
|
|
|
|
metadata: { message: 'hi copayer!' }
|
|
|
|
});
|
|
|
|
|
2014-04-09 07:05:25 -07:00
|
|
|
self._setupConnectionHandlers(dataConn, false, openCallback, closeCallback);
|
2014-04-08 14:35:43 -07:00
|
|
|
};
|
|
|
|
|
2014-04-15 08:17:28 -07:00
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
Network.prototype.disconnect = function(peerId, cb) {
|
|
|
|
console.log('[Network.js.268:disconnect:]'); //TODO
|
2014-04-08 14:35:43 -07:00
|
|
|
var self = this;
|
2014-04-12 09:37:41 -07:00
|
|
|
self.closing = 1;
|
2014-04-08 14:35:43 -07:00
|
|
|
|
2014-04-08 22:20:28 -07:00
|
|
|
this.send(null, { type: 'disconnect' }, function() {
|
2014-04-12 09:37:41 -07:00
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
console.log('[Network.js.273] disconnect CB'); //TODO
|
2014-04-08 14:35:43 -07:00
|
|
|
self.connectedPeers = [];
|
|
|
|
self.peerId = null;
|
|
|
|
if (self.peer) {
|
|
|
|
self.peer.disconnect();
|
|
|
|
self.peer.destroy();
|
|
|
|
self.peer = null;
|
|
|
|
}
|
2014-04-12 09:37:41 -07:00
|
|
|
self.closing = 0;
|
2014-04-08 14:35:43 -07:00
|
|
|
if (typeof cb === 'function') cb();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-04-14 11:31:10 -07:00
|
|
|
module.exports = require('soop')(Network);
|