|
@@ -64,20 +64,28 @@ function Peer(id, options) {
|
|
this.destroyed = false;
|
|
this.destroyed = false;
|
|
this.disconnected = false;
|
|
this.disconnected = false;
|
|
|
|
|
|
- // Connections for this peer.
|
|
|
|
|
|
+ // DataConnections for this peer.
|
|
this.connections = {};
|
|
this.connections = {};
|
|
|
|
+
|
|
|
|
+ // MediaConnections for this peer
|
|
|
|
+ this.calls = {}
|
|
|
|
+
|
|
// Connection managers.
|
|
// Connection managers.
|
|
- this.managers = {};
|
|
|
|
|
|
+ // peer => {
|
|
|
|
+ // nextId: unique number to use for next manager created
|
|
|
|
+ // dataManager: the last created data manager, for multiplexing data connections
|
|
|
|
+ // managers: { id: manager} }
|
|
|
|
+ // }
|
|
|
|
+ this._managers = {};
|
|
|
|
|
|
// Queued connections to make.
|
|
// Queued connections to make.
|
|
this._queued = [];
|
|
this._queued = [];
|
|
|
|
|
|
// Init immediately if ID is given, otherwise ask server for ID
|
|
// Init immediately if ID is given, otherwise ask server for ID
|
|
|
|
+ this.id = null;
|
|
if (id) {
|
|
if (id) {
|
|
- this.id = id;
|
|
|
|
- this._init();
|
|
|
|
|
|
+ this._initialize(id.toString());
|
|
} else {
|
|
} else {
|
|
- this.id = null;
|
|
|
|
this._retrieveId();
|
|
this._retrieveId();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
@@ -101,7 +109,7 @@ Peer.prototype._retrieveId = function(cb) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
self.id = http.responseText;
|
|
self.id = http.responseText;
|
|
- self._init();
|
|
|
|
|
|
+ self._initialize(self.id);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
http.send(null);
|
|
http.send(null);
|
|
@@ -111,8 +119,9 @@ Peer.prototype._retrieveId = function(cb) {
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
-Peer.prototype._init = function() {
|
|
|
|
|
|
+Peer.prototype._initialize = function(id) {
|
|
var self = this;
|
|
var self = this;
|
|
|
|
+ this.id = id;
|
|
this._socket = new Socket(this._options.host, this._options.port, this._options.key, this.id);
|
|
this._socket = new Socket(this._options.host, this._options.port, this._options.key, this.id);
|
|
this._socket.on('message', function(data) {
|
|
this._socket.on('message', function(data) {
|
|
self._handleServerJSONMessage(data);
|
|
self._handleServerJSONMessage(data);
|
|
@@ -132,7 +141,8 @@ Peer.prototype._init = function() {
|
|
|
|
|
|
Peer.prototype._handleServerJSONMessage = function(message) {
|
|
Peer.prototype._handleServerJSONMessage = function(message) {
|
|
var peer = message.src;
|
|
var peer = message.src;
|
|
- var manager = this.managers[peer];
|
|
|
|
|
|
+ var managerId = message.manager;
|
|
|
|
+ var manager = this._getManager(peer, managerId);
|
|
var payload = message.payload;
|
|
var payload = message.payload;
|
|
switch (message.type) {
|
|
switch (message.type) {
|
|
case 'OPEN':
|
|
case 'OPEN':
|
|
@@ -151,36 +161,36 @@ Peer.prototype._handleServerJSONMessage = function(message) {
|
|
labels: payload.labels,
|
|
labels: payload.labels,
|
|
config: this._options.config
|
|
config: this._options.config
|
|
};
|
|
};
|
|
-
|
|
|
|
- var manager = this.managers[peer];
|
|
|
|
|
|
+ // Either forward to or create new manager
|
|
|
|
+
|
|
if (!manager) {
|
|
if (!manager) {
|
|
- manager = new ConnectionManager(this.id, peer, this._socket, options);
|
|
|
|
- this._attachManagerListeners(manager);
|
|
|
|
- this.managers[peer] = manager;
|
|
|
|
- this.connections[peer] = manager.connections;
|
|
|
|
|
|
+ manager = this._createManager(managerId, peer, options);
|
|
}
|
|
}
|
|
manager.handleUpdate(options.labels);
|
|
manager.handleUpdate(options.labels);
|
|
manager.handleSDP(payload.sdp, message.type, payload.call);
|
|
manager.handleSDP(payload.sdp, message.type, payload.call);
|
|
break;
|
|
break;
|
|
case 'EXPIRE':
|
|
case 'EXPIRE':
|
|
- if (manager) {
|
|
|
|
- manager.close();
|
|
|
|
- manager.emit('error', new Error('Could not connect to peer ' + manager.peer));
|
|
|
|
- }
|
|
|
|
|
|
+ peer.emit('error', new Error('Could not connect to peer ' + manager.peer));
|
|
break;
|
|
break;
|
|
case 'ANSWER':
|
|
case 'ANSWER':
|
|
|
|
+ // Forward to specific manager
|
|
if (manager) {
|
|
if (manager) {
|
|
manager.handleSDP(payload.sdp, message.type);
|
|
manager.handleSDP(payload.sdp, message.type);
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case 'CANDIDATE':
|
|
case 'CANDIDATE':
|
|
|
|
+ // Forward to specific manager
|
|
if (manager) {
|
|
if (manager) {
|
|
manager.handleCandidate(payload);
|
|
manager.handleCandidate(payload);
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case 'LEAVE':
|
|
case 'LEAVE':
|
|
- if (manager) {
|
|
|
|
- manager.handleLeave();
|
|
|
|
|
|
+ // Leave on all managers for a user
|
|
|
|
+ if (this._managers[peer]) {
|
|
|
|
+ var ids = Object.keys(this._managers[peer].managers);
|
|
|
|
+ for (var i = 0; i < ids.length; i++) {
|
|
|
|
+ this._managers[peer].managers[ids[i]].handleLeave();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case 'INVALID-KEY':
|
|
case 'INVALID-KEY':
|
|
@@ -205,17 +215,20 @@ Peer.prototype._attachManagerListeners = function(manager) {
|
|
var self = this;
|
|
var self = this;
|
|
// Handle receiving a connection.
|
|
// Handle receiving a connection.
|
|
manager.on('connection', function(connection) {
|
|
manager.on('connection', function(connection) {
|
|
|
|
+ self._managers[manager.peer].dataManager = manager;
|
|
|
|
+ self.connections[connection.label] = connection;
|
|
self.emit('connection', connection);
|
|
self.emit('connection', connection);
|
|
});
|
|
});
|
|
// Handle receiving a call
|
|
// Handle receiving a call
|
|
manager.on('call', function(call) {
|
|
manager.on('call', function(call) {
|
|
|
|
+ self.calls[call.label] = call;
|
|
self.emit('call', call);
|
|
self.emit('call', call);
|
|
});
|
|
});
|
|
// Handle a connection closing.
|
|
// Handle a connection closing.
|
|
manager.on('close', function() {
|
|
manager.on('close', function() {
|
|
- if (!!self.managers[manager.peer]) {
|
|
|
|
- delete self.managers[manager.peer];
|
|
|
|
- delete self.connections[manager.peer];
|
|
|
|
|
|
+ if (!!self._managers[manager.peer]) {
|
|
|
|
+ delete self._managers[manager.peer];
|
|
|
|
+ // TODO: delete relevant calls and connections
|
|
}
|
|
}
|
|
});
|
|
});
|
|
manager.on('error', function(err) {
|
|
manager.on('error', function(err) {
|
|
@@ -223,33 +236,22 @@ Peer.prototype._attachManagerListeners = function(manager) {
|
|
});
|
|
});
|
|
};
|
|
};
|
|
|
|
|
|
-/** Destroys the Peer and emits an error message. */
|
|
|
|
-Peer.prototype._abort = function(type, message) {
|
|
|
|
- util.log('Aborting. Error:', message);
|
|
|
|
- var err = new Error(message);
|
|
|
|
- err.type = type;
|
|
|
|
- this.destroy();
|
|
|
|
- this.emit('error', err);
|
|
|
|
-};
|
|
|
|
|
|
|
|
-Peer.prototype._cleanup = function() {
|
|
|
|
- var self = this;
|
|
|
|
- if (!!this.managers) {
|
|
|
|
- var peers = Object.keys(this.managers);
|
|
|
|
- for (var i = 0, ii = peers.length; i < ii; i++) {
|
|
|
|
- this.managers[peers[i]].close();
|
|
|
|
- }
|
|
|
|
|
|
+Peer.prototype._getManager = function(peer, managerId) {
|
|
|
|
+ if (this._managers[peer]) {
|
|
|
|
+ return this._managers[peer].managers[managerId];
|
|
}
|
|
}
|
|
- util.setZeroTimeout(function(){
|
|
|
|
- self.disconnect();
|
|
|
|
- });
|
|
|
|
- this.emit('close');
|
|
|
|
-};
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
+Peer.prototype._getDataManager = function(peer) {
|
|
|
|
+ if (this._managers[peer]) {
|
|
|
|
+ return this._managers[peer].dataManager;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
/** Exposed connect function for users. Will try to connect later if user
|
|
/** Exposed connect function for users. Will try to connect later if user
|
|
* is waiting for an ID. */
|
|
* is waiting for an ID. */
|
|
-Peer.prototype._getManager = function(peer, options) {
|
|
|
|
|
|
+Peer.prototype._createManager = function(managerId, peer, options) {
|
|
if (this.disconnected) {
|
|
if (this.disconnected) {
|
|
var err = new Error('This Peer has been disconnected from the server and can no longer make connections.');
|
|
var err = new Error('This Peer has been disconnected from the server and can no longer make connections.');
|
|
err.type = 'server-disconnected';
|
|
err.type = 'server-disconnected';
|
|
@@ -261,54 +263,49 @@ Peer.prototype._getManager = function(peer, options) {
|
|
config: this._options.config
|
|
config: this._options.config
|
|
}, options);
|
|
}, options);
|
|
|
|
|
|
- var manager = this.managers[peer];
|
|
|
|
-
|
|
|
|
- // Firefox currently does not support multiplexing once an offer is made.
|
|
|
|
- if (util.browserisms === 'Firefox' && !!manager && manager.firefoxSingular) {
|
|
|
|
- var err = new Error('Firefox currently does not support renegotiating connections');
|
|
|
|
- err.type = 'firefoxism';
|
|
|
|
- this.emit('error', err);
|
|
|
|
- return;
|
|
|
|
|
|
+ if (!this._managers[peer]) {
|
|
|
|
+ this._managers[peer] = {nextId: 0, managers: {}};
|
|
}
|
|
}
|
|
-
|
|
|
|
- if (!manager) {
|
|
|
|
- manager = new ConnectionManager(this.id, peer, this._socket, options);
|
|
|
|
- this._attachManagerListeners(manager);
|
|
|
|
- this.managers[peer] = manager;
|
|
|
|
- this.connections[peer] = manager.connections;
|
|
|
|
|
|
+
|
|
|
|
+ managerId = managerId || peer + this._managers[peer].nextId++;
|
|
|
|
+
|
|
|
|
+ var manager = new ConnectionManager(managerId, peer, options);
|
|
|
|
+ if (!!this.id && !!this._socket) {
|
|
|
|
+ manager.initialize(this.id, this._socket);
|
|
|
|
+ } else {
|
|
|
|
+ this._queued.push(manager);
|
|
}
|
|
}
|
|
|
|
+ this._attachManagerListeners(manager);
|
|
|
|
+ this._managers[peer].managers[manager._managerId] = manager;
|
|
|
|
|
|
return manager;
|
|
return manager;
|
|
};
|
|
};
|
|
|
|
|
|
Peer.prototype.connect = function(peer, options) {
|
|
Peer.prototype.connect = function(peer, options) {
|
|
- var manager = this._getManager(peer, options);
|
|
|
|
- if (manager) {
|
|
|
|
- var connection = manager.connect(options);
|
|
|
|
- if (!this.id) {
|
|
|
|
- this._queued.push(manager);
|
|
|
|
- }
|
|
|
|
- return connection;
|
|
|
|
|
|
+ var manager = this._getDataManager(peer);
|
|
|
|
+ if (!manager) {
|
|
|
|
+ manager = this._createManager(false, peer, options);
|
|
}
|
|
}
|
|
|
|
+ var connection = manager.connect(options);
|
|
|
|
+ return connection;
|
|
}
|
|
}
|
|
|
|
|
|
Peer.prototype.call = function(peer, stream, options) {
|
|
Peer.prototype.call = function(peer, stream, options) {
|
|
- var manager = this._getManager(peer, options);
|
|
|
|
- if (manager) {
|
|
|
|
- var connection = manager.call(stream, options);
|
|
|
|
- if (!this.id) {
|
|
|
|
- this._queued.push(manager);
|
|
|
|
- }
|
|
|
|
- return connection;
|
|
|
|
- }
|
|
|
|
|
|
+ var manager = this._createManager(false, peer, options);
|
|
|
|
+ var connection = manager.call(stream, options);
|
|
|
|
+ return connection;
|
|
}
|
|
}
|
|
|
|
|
|
-/**
|
|
|
|
- * Return the peer id or null, if there's no id at the moment.
|
|
|
|
- * Reasons for no id could be 'connect in progress' or 'disconnected'
|
|
|
|
- */
|
|
|
|
-Peer.prototype.getId = function() {
|
|
|
|
- return this.id;
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/** Destroys the Peer and emits an error message. */
|
|
|
|
+Peer.prototype._abort = function(type, message) {
|
|
|
|
+ util.log('Aborting. Error:', message);
|
|
|
|
+ var err = new Error(message);
|
|
|
|
+ err.type = type;
|
|
|
|
+ this.destroy();
|
|
|
|
+ this.emit('error', err);
|
|
};
|
|
};
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -324,6 +321,23 @@ Peer.prototype.destroy = function() {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+Peer.prototype._cleanup = function() {
|
|
|
|
+ var self = this;
|
|
|
|
+ if (!!this._managers) {
|
|
|
|
+ var peers = Object.keys(this._managers);
|
|
|
|
+ for (var i = 0, ii = peers.length; i < ii; i++) {
|
|
|
|
+ var ids = Object.keys(this._managers[peers[i]]);
|
|
|
|
+ for (var j = 0, jj = peers.length; j < jj; j++) {
|
|
|
|
+ this._managers[peers[i]][ids[j]].close();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ util.setZeroTimeout(function(){
|
|
|
|
+ self.disconnect();
|
|
|
|
+ });
|
|
|
|
+ this.emit('close');
|
|
|
|
+};
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Disconnects the Peer's connection to the PeerServer. Does not close any
|
|
* Disconnects the Peer's connection to the PeerServer. Does not close any
|
|
* active connections.
|
|
* active connections.
|
|
@@ -343,19 +357,5 @@ Peer.prototype.disconnect = function() {
|
|
/** The current browser. */
|
|
/** The current browser. */
|
|
Peer.browser = util.browserisms;
|
|
Peer.browser = util.browserisms;
|
|
|
|
|
|
-/**
|
|
|
|
- * Provides a clean method for checking if there's an active connection to the
|
|
|
|
- * peer server.
|
|
|
|
- */
|
|
|
|
-Peer.prototype.isConnected = function() {
|
|
|
|
- return !this.disconnected;
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * Returns true if this peer is destroyed and can no longer be used.
|
|
|
|
- */
|
|
|
|
-Peer.prototype.isDestroyed = function() {
|
|
|
|
- return this.destroyed;
|
|
|
|
-};
|
|
|
|
|
|
|
|
exports.Peer = Peer;
|
|
exports.Peer = Peer;
|