|
@@ -2,292 +2,327 @@
|
|
|
* A peer who can initiate connections with other peers.
|
|
|
*/
|
|
|
function Peer(id, options) {
|
|
|
+ if (!(this instanceof Peer)) return new Peer(id, options);
|
|
|
+ EventEmitter.call(this);
|
|
|
+
|
|
|
+ // Deal with overloading
|
|
|
if (id && id.constructor == Object) {
|
|
|
options = id;
|
|
|
id = undefined;
|
|
|
+ } else if (id) {
|
|
|
+ // Ensure id is a string
|
|
|
+ id = id.toString();
|
|
|
}
|
|
|
- if (!(this instanceof Peer)) return new Peer(id, options);
|
|
|
- EventEmitter.call(this);
|
|
|
-
|
|
|
+ //
|
|
|
|
|
|
+ // Configurize options
|
|
|
options = util.extend({
|
|
|
- debug: false,
|
|
|
- host: '0.peerjs.com',
|
|
|
- port: 9000,
|
|
|
+ debug: 0, // 1: Errors, 2: Warnings, 3: All logs
|
|
|
+ host: util.CLOUD_HOST,
|
|
|
+ port: util.CLOUD_PORT,
|
|
|
key: 'peerjs',
|
|
|
- config: { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }] }
|
|
|
+ config: util.defaultConfig
|
|
|
}, options);
|
|
|
- this._options = options;
|
|
|
- util.debug = options.debug;
|
|
|
-
|
|
|
- // First check if browser can use PeerConnection/DataChannels.
|
|
|
- // TODO: when media is supported, lower browser version limit and move DC
|
|
|
- // check to where`connect` is called.
|
|
|
- var self = this;
|
|
|
- if (!util.isBrowserCompatible()) {
|
|
|
- util.setZeroTimeout(function() {
|
|
|
- self._abort('browser-incompatible', 'The current browser does not support WebRTC DataChannels');
|
|
|
- });
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
+ this.options = options;
|
|
|
// Detect relative URL host.
|
|
|
if (options.host === '/') {
|
|
|
options.host = window.location.hostname;
|
|
|
}
|
|
|
+ // Set whether we use SSL to same as current host
|
|
|
+ if (options.secure === undefined && options.host !== util.CLOUD_HOST) {
|
|
|
+ options.secure = util.isSecure();
|
|
|
+ }
|
|
|
+ // Set a custom log function if present
|
|
|
+ if (options.logFunction) {
|
|
|
+ util.setLogFunction(options.logFunction);
|
|
|
+ }
|
|
|
+ util.setLogLevel(options.debug);
|
|
|
+ //
|
|
|
|
|
|
- // Ensure alphanumeric_-
|
|
|
- if (id && !/^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(id)) {
|
|
|
- util.setZeroTimeout(function() {
|
|
|
- self._abort('invalid-id', 'ID "' + id + '" is invalid');
|
|
|
- });
|
|
|
+ // Sanity checks
|
|
|
+ // Ensure WebRTC supported
|
|
|
+ if (!util.supports.audioVideo && !util.supports.data ) {
|
|
|
+ this._delayedAbort('browser-incompatible', 'The current browser does not support WebRTC');
|
|
|
return;
|
|
|
}
|
|
|
- if (options.key && !/^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(options.key)) {
|
|
|
- util.setZeroTimeout(function() {
|
|
|
- self._abort('invalid-key', 'API KEY "' + options.key + '" is invalid');
|
|
|
- });
|
|
|
+ // Ensure alphanumeric id
|
|
|
+ if (!util.validateId(id)) {
|
|
|
+ this._delayedAbort('invalid-id', 'ID "' + id + '" is invalid');
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- this._secure = util.isSecure();
|
|
|
- // Errors for now because no support for SSL on cloud server.
|
|
|
- if (this._secure && options.host === '0.peerjs.com') {
|
|
|
- util.setZeroTimeout(function() {
|
|
|
- self._abort('ssl-unavailable',
|
|
|
- 'The cloud server currently does not support HTTPS. Please run your own PeerServer to use HTTPS.');
|
|
|
- });
|
|
|
+ // Ensure valid key
|
|
|
+ if (!util.validateKey(options.key)) {
|
|
|
+ this._delayedAbort('invalid-key', 'API KEY "' + options.key + '" is invalid');
|
|
|
return;
|
|
|
}
|
|
|
+ // Ensure not using unsecure cloud server on SSL page
|
|
|
+ if (options.secure && options.host === '0.peerjs.com') {
|
|
|
+ this._delayedAbort('ssl-unavailable',
|
|
|
+ 'The cloud server currently does not support HTTPS. Please run your own PeerServer to use HTTPS.');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //
|
|
|
|
|
|
// States.
|
|
|
- this.destroyed = false;
|
|
|
- this.disconnected = false;
|
|
|
-
|
|
|
- // Connections for this peer.
|
|
|
- this.connections = {};
|
|
|
- // Connection managers.
|
|
|
- this.managers = {};
|
|
|
-
|
|
|
- // Queued connections to make.
|
|
|
- this._queued = [];
|
|
|
+ this.destroyed = false; // Connections have been killed
|
|
|
+ this.disconnected = false; // Connection to PeerServer killed manually but P2P connections still active
|
|
|
+ this.open = false; // Sockets and such are not yet open.
|
|
|
+ //
|
|
|
+
|
|
|
+ // References
|
|
|
+ this.connections = {}; // DataConnections for this peer.
|
|
|
+ this._lostMessages = {}; // src => [list of messages]
|
|
|
+ //
|
|
|
+
|
|
|
+ // Initialize the 'socket' (which is actually a mix of XHR streaming and
|
|
|
+ // websockets.)
|
|
|
+ var self = this;
|
|
|
+ this.socket = new Socket(this.options.secure, this.options.host, this.options.port, this.options.key);
|
|
|
+ this.socket.on('message', function(data) {
|
|
|
+ self._handleMessage(data);
|
|
|
+ });
|
|
|
+ this.socket.on('error', function(error) {
|
|
|
+ self._abort('socket-error', error);
|
|
|
+ });
|
|
|
+ this.socket.on('close', function() {
|
|
|
+ if (!self.disconnected) { // If we haven't explicitly disconnected, emit error.
|
|
|
+ self._abort('socket-closed', 'Underlying socket is already closed.');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ //
|
|
|
|
|
|
- // Init immediately if ID is given, otherwise ask server for ID
|
|
|
+ // Start the connections
|
|
|
if (id) {
|
|
|
- this.id = id;
|
|
|
- this._init();
|
|
|
+ this._initialize(id);
|
|
|
} else {
|
|
|
- this.id = null;
|
|
|
this._retrieveId();
|
|
|
}
|
|
|
+ //
|
|
|
};
|
|
|
|
|
|
util.inherits(Peer, EventEmitter);
|
|
|
|
|
|
+/** Get a unique ID from the server via XHR. */
|
|
|
Peer.prototype._retrieveId = function(cb) {
|
|
|
var self = this;
|
|
|
- try {
|
|
|
- var http = new XMLHttpRequest();
|
|
|
- var protocol = this._secure ? 'https://' : 'http://';
|
|
|
- var url = protocol + this._options.host + ':' + this._options.port + '/' + this._options.key + '/id';
|
|
|
- var queryString = '?ts=' + new Date().getTime() + '' + Math.random();
|
|
|
- url += queryString;
|
|
|
- // If there's no ID we need to wait for one before trying to init socket.
|
|
|
- http.open('get', url, true);
|
|
|
- http.onreadystatechange = function() {
|
|
|
- if (http.readyState === 4) {
|
|
|
- if (http.status !== 200) {
|
|
|
- throw 'Retrieve ID response not 200';
|
|
|
- return;
|
|
|
- }
|
|
|
- self.id = http.responseText;
|
|
|
- self._init();
|
|
|
- }
|
|
|
- };
|
|
|
- http.send(null);
|
|
|
- } catch(e) {
|
|
|
- this._abort('server-error', 'Could not get an ID from the server');
|
|
|
+ var http = new XMLHttpRequest();
|
|
|
+ var protocol = this.options.secure ? 'https://' : 'http://';
|
|
|
+ var url = protocol + this.options.host + ':' + this.options.port + '/' + this.options.key + '/id';
|
|
|
+ var queryString = '?ts=' + new Date().getTime() + '' + Math.random();
|
|
|
+ url += queryString;
|
|
|
+
|
|
|
+ // If there's no ID we need to wait for one before trying to init socket.
|
|
|
+ http.open('get', url, true);
|
|
|
+ http.onerror = function(e) {
|
|
|
+ util.error('Error retrieving ID', e);
|
|
|
+ self._abort('server-error', 'Could not get an ID from the server');
|
|
|
}
|
|
|
+ http.onreadystatechange = function() {
|
|
|
+ if (http.readyState !== 4) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (http.status !== 200) {
|
|
|
+ http.onerror();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ self._initialize(http.responseText);
|
|
|
+ };
|
|
|
+ http.send(null);
|
|
|
};
|
|
|
|
|
|
-
|
|
|
-Peer.prototype._init = function() {
|
|
|
+/** Initialize a connection with the server. */
|
|
|
+Peer.prototype._initialize = function(id) {
|
|
|
var self = this;
|
|
|
- this._socket = new Socket(this._options.host, this._options.port, this._options.key, this.id);
|
|
|
- this._socket.on('message', function(data) {
|
|
|
- self._handleServerJSONMessage(data);
|
|
|
- });
|
|
|
- this._socket.on('error', function(error) {
|
|
|
- util.log(error);
|
|
|
- self._abort('socket-error', error);
|
|
|
- });
|
|
|
- this._socket.on('close', function() {
|
|
|
- var msg = 'Underlying socket has closed';
|
|
|
- util.log('error', msg);
|
|
|
- self._abort('socket-closed', msg);
|
|
|
- });
|
|
|
- this._socket.start();
|
|
|
+ this.id = id;
|
|
|
+ this.socket.start(this.id);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-Peer.prototype._handleServerJSONMessage = function(message) {
|
|
|
- var peer = message.src;
|
|
|
- var manager = this.managers[peer];
|
|
|
+/** Handles messages from the server. */
|
|
|
+Peer.prototype._handleMessage = function(message) {
|
|
|
+ var type = message.type;
|
|
|
var payload = message.payload;
|
|
|
- switch (message.type) {
|
|
|
- case 'OPEN':
|
|
|
- this._processQueue();
|
|
|
+ var peer = message.src;
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case 'OPEN': // The connection to the server is open.
|
|
|
this.emit('open', this.id);
|
|
|
+ this.open = true;
|
|
|
break;
|
|
|
- case 'ERROR':
|
|
|
+ case 'ERROR': // Server error.
|
|
|
this._abort('server-error', payload.msg);
|
|
|
break;
|
|
|
- case 'ID-TAKEN':
|
|
|
- this._abort('unavailable-id', 'ID `'+this.id+'` is taken');
|
|
|
- break;
|
|
|
- case 'OFFER':
|
|
|
- var options = {
|
|
|
- sdp: payload.sdp,
|
|
|
- labels: payload.labels,
|
|
|
- config: this._options.config
|
|
|
- };
|
|
|
-
|
|
|
- var manager = this.managers[peer];
|
|
|
- if (!manager) {
|
|
|
- manager = new ConnectionManager(this.id, peer, this._socket, options);
|
|
|
- this._attachManagerListeners(manager);
|
|
|
- this.managers[peer] = manager;
|
|
|
- this.connections[peer] = manager.connections;
|
|
|
- }
|
|
|
- manager.update(options.labels);
|
|
|
- manager.handleSDP(payload.sdp, message.type);
|
|
|
+ case 'ID-TAKEN': // The selected ID is taken.
|
|
|
+ this._abort('unavailable-id', 'ID `' + this.id + '` is taken');
|
|
|
break;
|
|
|
- case 'EXPIRE':
|
|
|
- if (manager) {
|
|
|
- manager.close();
|
|
|
- manager.emit('error', new Error('Could not connect to peer ' + manager.peer));
|
|
|
- }
|
|
|
+ case 'INVALID-KEY': // The given API key cannot be found.
|
|
|
+ this._abort('invalid-key', 'API KEY "' + this._key + '" is invalid');
|
|
|
break;
|
|
|
- case 'ANSWER':
|
|
|
- if (manager) {
|
|
|
- manager.handleSDP(payload.sdp, message.type);
|
|
|
- }
|
|
|
+
|
|
|
+ //
|
|
|
+ case 'LEAVE': // Another peer has closed its connection to this peer.
|
|
|
+ util.log('Received leave message from', peer);
|
|
|
+ this._cleanupPeer(peer);
|
|
|
break;
|
|
|
- case 'CANDIDATE':
|
|
|
- if (manager) {
|
|
|
- manager.handleCandidate(payload);
|
|
|
- }
|
|
|
+
|
|
|
+ case 'EXPIRE': // The offer sent to a peer has expired without response.
|
|
|
+ this.emit('error', new Error('Could not connect to peer ' + peer));
|
|
|
break;
|
|
|
- case 'LEAVE':
|
|
|
- if (manager) {
|
|
|
- manager.handleLeave();
|
|
|
+ case 'OFFER': // we should consider switching this to CALL/CONNECT, but this is the least breaking option.
|
|
|
+ var connectionId = payload.connectionId;
|
|
|
+ var connection = this.getConnection(peer, connectionId);
|
|
|
+
|
|
|
+ if (connection) {
|
|
|
+ util.warn('Offer received for existing Connection ID:', connectionId);
|
|
|
+ //connection.handleMessage(message);
|
|
|
+ } else {
|
|
|
+ // Create a new connection.
|
|
|
+ if (payload.type === 'media') {
|
|
|
+ var connection = new MediaConnection(peer, this, {
|
|
|
+ connectionId: connectionId,
|
|
|
+ _payload: payload,
|
|
|
+ metadata: payload.metadata,
|
|
|
+ });
|
|
|
+ this._addConnection(peer, connection);
|
|
|
+ this.emit('call', connection);
|
|
|
+ } else if (payload.type === 'data') {
|
|
|
+ connection = new DataConnection(peer, this, {
|
|
|
+ connectionId: connectionId,
|
|
|
+ _payload: payload,
|
|
|
+ metadata: payload.metadata,
|
|
|
+ label: payload.label,
|
|
|
+ serialization: payload.serialization,
|
|
|
+ reliable: payload.reliable
|
|
|
+ });
|
|
|
+ this._addConnection(peer, connection);
|
|
|
+ this.emit('connection', connection);
|
|
|
+ } else {
|
|
|
+ util.warn('Received malformed connection type:', payload.type);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // Find messages.
|
|
|
+ var messages = this._getMessages(connectionId);
|
|
|
+ for (var i = 0, ii = messages.length; i < ii; i += 1) {
|
|
|
+ connection.handleMessage(messages[i]);
|
|
|
+ }
|
|
|
}
|
|
|
break;
|
|
|
- case 'INVALID-KEY':
|
|
|
- this._abort('invalid-key', 'API KEY "' + this._key + '" is invalid');
|
|
|
- break;
|
|
|
default:
|
|
|
- util.log('Unrecognized message type:', message.type);
|
|
|
+ if (!payload) {
|
|
|
+ util.warn('You received a malformed message from ' + peer + ' of type ' + type);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var id = payload.connectionId;
|
|
|
+ var connection = this.getConnection(peer, id);
|
|
|
+
|
|
|
+ if (connection && connection.pc) {
|
|
|
+ // Pass it on.
|
|
|
+ connection.handleMessage(message);
|
|
|
+ } else if (id) {
|
|
|
+ // Store for possible later use
|
|
|
+ this._storeMessage(id, message);
|
|
|
+ } else {
|
|
|
+ util.warn('You received an unrecognized message:', message);
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
-};
|
|
|
+}
|
|
|
|
|
|
-/** Process queued calls to connect. */
|
|
|
-Peer.prototype._processQueue = function() {
|
|
|
- while (this._queued.length > 0) {
|
|
|
- var manager = this._queued.pop();
|
|
|
- manager.initialize(this.id, this._socket);
|
|
|
+/** Stores messages without a set up connection, to be claimed later. */
|
|
|
+Peer.prototype._storeMessage = function(connectionId, message) {
|
|
|
+ if (!this._lostMessages[connectionId]) {
|
|
|
+ this._lostMessages[connectionId] = [];
|
|
|
}
|
|
|
-};
|
|
|
-
|
|
|
-/** Listeners for manager. */
|
|
|
-Peer.prototype._attachManagerListeners = function(manager) {
|
|
|
- var self = this;
|
|
|
- // Handle receiving a connection.
|
|
|
- manager.on('connection', function(connection) {
|
|
|
- self.emit('connection', connection);
|
|
|
- });
|
|
|
- // Handle a connection closing.
|
|
|
- manager.on('close', function() {
|
|
|
- if (!!self.managers[manager.peer]) {
|
|
|
- delete self.managers[manager.peer];
|
|
|
- delete self.connections[manager.peer];
|
|
|
- }
|
|
|
- });
|
|
|
- manager.on('error', function(err) {
|
|
|
- self.emit('error', err);
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
-/** 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);
|
|
|
-};
|
|
|
+ this._lostMessages[connectionId].push(message);
|
|
|
+}
|
|
|
|
|
|
-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();
|
|
|
- }
|
|
|
+/** Retrieve messages from lost message store */
|
|
|
+Peer.prototype._getMessages = function(connectionId) {
|
|
|
+ var messages = this._lostMessages[connectionId];
|
|
|
+ if (messages) {
|
|
|
+ delete this._lostMessages[connectionId];
|
|
|
+ return messages;
|
|
|
+ } else {
|
|
|
+ return [];
|
|
|
}
|
|
|
- util.setZeroTimeout(function(){
|
|
|
- self.disconnect();
|
|
|
- });
|
|
|
- this.emit('close');
|
|
|
-};
|
|
|
-
|
|
|
+}
|
|
|
|
|
|
-/** Exposed connect function for users. Will try to connect later if user
|
|
|
- * is waiting for an ID. */
|
|
|
+/**
|
|
|
+ * Returns a DataConnection to the specified peer. See documentation for a
|
|
|
+ * complete list of options.
|
|
|
+ */
|
|
|
Peer.prototype.connect = function(peer, options) {
|
|
|
if (this.disconnected) {
|
|
|
- var err = new Error('This Peer has been disconnected from the server and can no longer make connections.');
|
|
|
- err.type = 'server-disconnected';
|
|
|
- this.emit('error', err);
|
|
|
+ util.warn('You cannot connect to a new Peer because you called '
|
|
|
+ + '.disconnect() on this Peer and ended your connection with the'
|
|
|
+ + ' server. You can create a new Peer to reconnect.');
|
|
|
+ this.emit('error', new Error('Cannot connect to new Peer after disconnecting from server.'));
|
|
|
return;
|
|
|
}
|
|
|
+ var connection = new DataConnection(peer, this, options);
|
|
|
+ this._addConnection(peer, connection);
|
|
|
+ return connection;
|
|
|
+}
|
|
|
|
|
|
- options = util.extend({
|
|
|
- config: this._options.config
|
|
|
- }, 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 multiplexing after a DataChannel has already been established');
|
|
|
- err.type = 'firefoxism';
|
|
|
- this.emit('error', err);
|
|
|
+/**
|
|
|
+ * Returns a MediaConnection to the specified peer. See documentation for a
|
|
|
+ * complete list of options.
|
|
|
+ */
|
|
|
+Peer.prototype.call = function(peer, stream, options) {
|
|
|
+ if (this.disconnected) {
|
|
|
+ util.warn('You cannot connect to a new Peer because you called '
|
|
|
+ + '.disconnect() on this Peer and ended your connection with the'
|
|
|
+ + ' server. You can create a new Peer to reconnect.');
|
|
|
+ this.emit('error', new Error('Cannot connect to new Peer after disconnecting from server.'));
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- if (!manager) {
|
|
|
- manager = new ConnectionManager(this.id, peer, this._socket, options);
|
|
|
- this._attachManagerListeners(manager);
|
|
|
- this.managers[peer] = manager;
|
|
|
- this.connections[peer] = manager.connections;
|
|
|
+ if (!stream) {
|
|
|
+ util.error('To call a peer, you must provide a stream from your browser\'s `getUserMedia`.');
|
|
|
+ return;
|
|
|
}
|
|
|
+ options = options || {};
|
|
|
+ options._stream = stream;
|
|
|
+ var call = new MediaConnection(peer, this, options);
|
|
|
+ this._addConnection(peer, call);
|
|
|
+ return call;
|
|
|
+}
|
|
|
|
|
|
- var connection = manager.connect(options);
|
|
|
+/** Add a data/media connection to this peer. */
|
|
|
+Peer.prototype._addConnection = function(peer, connection) {
|
|
|
+ if (!this.connections[peer]) {
|
|
|
+ this.connections[peer] = [];
|
|
|
+ }
|
|
|
+ this.connections[peer].push(connection);
|
|
|
+}
|
|
|
|
|
|
- if (!this.id) {
|
|
|
- this._queued.push(manager);
|
|
|
+/** Retrieve a data/media connection for this peer. */
|
|
|
+Peer.prototype.getConnection = function(peer, id) {
|
|
|
+ var connections = this.connections[peer];
|
|
|
+ if (!connections) {
|
|
|
+ return null;
|
|
|
}
|
|
|
- return connection;
|
|
|
-};
|
|
|
+ for (var i = 0, ii = connections.length; i < ii; i++) {
|
|
|
+ if (connections[i].id === id) {
|
|
|
+ return connections[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+}
|
|
|
|
|
|
-/**
|
|
|
- * 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;
|
|
|
+Peer.prototype._delayedAbort = function(type, message) {
|
|
|
+ var self = this;
|
|
|
+ util.setZeroTimeout(function(){
|
|
|
+ self._abort(type, message);
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** Destroys the Peer and emits an error message. */
|
|
|
+Peer.prototype._abort = function(type, message) {
|
|
|
+ util.error('Aborting. Error:', message);
|
|
|
+ var err = new Error(message);
|
|
|
+ err.type = type;
|
|
|
+ this.destroy();
|
|
|
+ this.emit('error', err);
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -299,9 +334,29 @@ Peer.prototype.getId = function() {
|
|
|
Peer.prototype.destroy = function() {
|
|
|
if (!this.destroyed) {
|
|
|
this._cleanup();
|
|
|
+ this.disconnect();
|
|
|
this.destroyed = true;
|
|
|
}
|
|
|
-};
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/** Disconnects every connection on this peer. */
|
|
|
+Peer.prototype._cleanup = function() {
|
|
|
+ var peers = Object.keys(this.connections);
|
|
|
+ for (var i = 0, ii = peers.length; i < ii; i++) {
|
|
|
+ this._cleanupPeer(peers[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.emit('close');
|
|
|
+}
|
|
|
+
|
|
|
+/** Closes all connections to this peer. */
|
|
|
+Peer.prototype._cleanupPeer = function(peer) {
|
|
|
+ var connections = this.connections[peer];
|
|
|
+ for (var j = 0, jj = connections.length; j < jj; j += 1) {
|
|
|
+ connections[j].close();
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
/**
|
|
|
* Disconnects the Peer's connection to the PeerServer. Does not close any
|
|
@@ -310,31 +365,17 @@ Peer.prototype.destroy = function() {
|
|
|
* disconnected. It also cannot reconnect to the server.
|
|
|
*/
|
|
|
Peer.prototype.disconnect = function() {
|
|
|
- if (!this.disconnected) {
|
|
|
- if (!!this._socket) {
|
|
|
- this._socket.close();
|
|
|
+ var self = this;
|
|
|
+ util.setZeroTimeout(function(){
|
|
|
+ if (!self.disconnected) {
|
|
|
+ self.disconnected = true;
|
|
|
+ self.open = false;
|
|
|
+ if (self.socket) {
|
|
|
+ self.socket.close();
|
|
|
+ }
|
|
|
+ self.id = null;
|
|
|
}
|
|
|
- this.id = null;
|
|
|
- this.disconnected = true;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-/** The current browser. */
|
|
|
-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;
|