copay/js/models/network/WebRTC.js

321 lines
7.8 KiB
JavaScript
Raw Normal View History

2014-04-08 14:35:43 -07:00
var imports = require('soop').imports();
var EventEmitter= imports.EventEmitter || require('events').EventEmitter;
/*
* 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-17 12:58:07 -07:00
var self = this;
2014-04-09 07:05:25 -07:00
opts = opts || {};
this.peerId = opts.peerId;
this.apiKey = opts.apiKey || 'lwjd5qra8257b9';
this.debug = opts.debug || 3;
2014-04-18 14:25:51 -07:00
this.maxPeers = opts.maxPeers || 10;
2014-04-17 12:58:07 -07:00
this.opts = { key: opts.key };
// For using your own peerJs server
2014-04-17 12:58:07 -07:00
['port', 'host', 'path', 'debug'].forEach(function(k) {
if (opts[k]) self.opts[k]=opts[k];
});
2014-04-08 14:35:43 -07:00
this.connectedPeers = [];
2014-04-18 10:40:16 -07:00
this.started = false;
2014-04-08 14:35:43 -07:00
}
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;
};
2014-04-14 11:31:10 -07:00
Network.prototype._onClose = function(peerId) {
this.connectedPeers = Network._arrayRemove(peerId, this.connectedPeers);
2014-04-17 07:46:49 -07:00
this._notifyNetworkChange();
2014-04-08 14:35:43 -07:00
};
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;
};
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':
this._connectToPeers(obj.data.peers);
2014-04-17 07:46:49 -07:00
this._notifyNetworkChange();
2014-04-08 14:35:43 -07:00
break;
case 'disconnect':
this._onClose(obj.sender);
break;
2014-04-16 16:58:57 -07:00
case 'walletId':
2014-04-18 14:25:51 -07:00
this.emit('walletId', obj.data);
2014-04-16 16:58:57 -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-17 12:50:48 -07:00
Network.prototype._checkAnyPeer = function() {
if (!this.connectedPeers.length) {
console.log('EMIT openError: no more peers, not even you!');
this.emit('openError');
}
}
2014-04-16 13:50:10 -07:00
Network.prototype._setupConnectionHandlers = function(dataConn, isInbound) {
2014-04-09 07:05:25 -07:00
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-17 07:46:49 -07:00
self._notifyNetworkChange( isInbound ? dataConn.peer : null);
2014-04-16 13:50:10 -07:00
this.emit('open');
2014-04-08 14:35:43 -07:00
}
});
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-17 12:50:48 -07:00
self.emit('dataError');
2014-04-08 14:35:43 -07:00
});
dataConn.on('close', function() {
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-17 12:50:48 -07:00
self._checkAnyPeer();
2014-04-08 14:35:43 -07:00
});
};
2014-04-17 07:46:49 -07:00
Network.prototype._notifyNetworkChange = function(newPeer) {
console.log('[WebRTC.js.164:_notifyNetworkChange:]', newPeer); //TODO
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) {
self.peerId = peerId;
self.connectedPeers = [peerId];
return openCallback(peerId);
});
p.on('error', function(err) {
console.log('### PEER ERROR:', err);
2014-04-18 14:25:51 -07:00
//self.disconnect(null, true); // force disconnect
2014-04-17 12:50:48 -07:00
self._checkAnyPeer();
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-18 10:40:16 -07:00
Network.prototype.start = function(openCallback, opts) {
2014-04-18 14:25:51 -07:00
opts = opts || {};
2014-04-08 14:35:43 -07:00
// Start PeerJS Peer
2014-04-18 14:25:51 -07:00
var self = this;
if (this.started) {
// network already started, restarting network layer
opts.connectedPeers = this.connectedPeers;
Network._arrayRemove(this.peerId, opts.connectedPeers);
this.disconnect(function() {
self.start(openCallback, opts);
}, true); // fast disconnect
return;
}
2014-04-18 10:40:16 -07:00
opts = opts || {};
2014-04-18 14:25:51 -07:00
opts.connectedPeers = opts.connectedPeers || [];
2014-04-18 10:40:16 -07:00
this.peerId = this.peerId || opts.peerId;
2014-04-16 18:12:30 -07:00
2014-04-17 12:58:07 -07:00
this.peer = new Peer(this.peerId, this.opts);
2014-04-08 14:35:43 -07:00
this._setupPeerHandlers(openCallback);
2014-04-18 14:25:51 -07:00
for (var i = 0; i<opts.connectedPeers.length; i++) {
var otherPeerId = opts.connectedPeers[i];
this.connectTo(otherPeerId);
}
2014-04-18 10:40:16 -07:00
this.started = true;
2014-04-08 14:35:43 -07:00
};
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) {
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;
console.log('[WebRTC.js.242] SENDING ', data.type); //TODO
if (!peerIds) {
peerIds = this.connectedPeers;
data.isBroadcast = 1;
}
2014-04-08 14:35:43 -07:00
if (Array.isArray(peerIds)) {
var l = peerIds.length;
var i = 0;
peerIds.forEach(function(peerId) {
2014-04-17 12:50:48 -07:00
console.log('[WebRTC.js.258:peerId:]',peerId); //TODO
2014-04-08 14:35:43 -07:00
self._sendToOne(peerId, data, function () {
if (++i === l && typeof cb === 'function') cb();
});
});
}
else if (typeof peerIds === 'string')
self._sendToOne(peerIds, data, cb);
};
2014-04-16 13:50:10 -07:00
Network.prototype.connectTo = function(peerId) {
2014-04-08 14:35:43 -07:00
var self = this;
console.log('### STARTING TO CONNECT TO:' + peerId );
var dataConn = this.peer.connect(peerId, {
serialization: 'none',
reliable: true,
metadata: { message: 'hi copayer!' }
});
2014-04-16 13:50:10 -07:00
self._setupConnectionHandlers(dataConn, false);
2014-04-08 14:35:43 -07:00
};
2014-04-15 08:17:28 -07:00
2014-04-18 14:25:51 -07:00
Network.prototype.disconnect = function(cb, forced) {
2014-04-08 14:35:43 -07:00
var self = this;
self.closing = 1;
2014-04-18 14:25:51 -07:00
var cleanUp = function() {
2014-04-08 14:35:43 -07:00
self.connectedPeers = [];
2014-04-18 14:25:51 -07:00
self.started = false;
2014-04-08 14:35:43 -07:00
self.peerId = null;
if (self.peer) {
self.peer.disconnect();
self.peer.destroy();
self.peer = null;
}
self.closing = 0;
2014-04-08 14:35:43 -07:00
if (typeof cb === 'function') cb();
2014-04-18 14:25:51 -07:00
};
if (!forced) {
this.send(null, { type: 'disconnect' }, cleanUp);
} else {
cleanUp();
}
2014-04-08 14:35:43 -07:00
};
2014-04-14 11:31:10 -07:00
module.exports = require('soop')(Network);