|
@@ -589,7 +589,7 @@ EventEmitter.prototype.addListener = function(type, listener, scope, once) {
|
|
// Adding the second element, need to change to array.
|
|
// Adding the second element, need to change to array.
|
|
this._events[type] = [this._events[type], listener];
|
|
this._events[type] = [this._events[type], listener];
|
|
}
|
|
}
|
|
- return this;
|
|
|
|
|
|
+
|
|
};
|
|
};
|
|
|
|
|
|
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
|
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
|
@@ -839,14 +839,13 @@ function Peer(id, options) {
|
|
options = util.extend({
|
|
options = util.extend({
|
|
debug: false,
|
|
debug: false,
|
|
host: '0.peerjs.com',
|
|
host: '0.peerjs.com',
|
|
- config: { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }] },
|
|
|
|
- port: 9000
|
|
|
|
|
|
+ port: 9000,
|
|
|
|
+ key: 'peerjs',
|
|
|
|
+ config: { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }] }
|
|
}, options);
|
|
}, options);
|
|
this._options = options;
|
|
this._options = options;
|
|
util.debug = options.debug;
|
|
util.debug = options.debug;
|
|
|
|
|
|
- this._server = options.host + ':' + options.port;
|
|
|
|
-
|
|
|
|
// Ensure alphanumeric_-
|
|
// Ensure alphanumeric_-
|
|
if (id && !/^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(id)) {
|
|
if (id && !/^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(id)) {
|
|
throw new Error('Peer ID can only contain alphanumerics, "_", and "-".');
|
|
throw new Error('Peer ID can only contain alphanumerics, "_", and "-".');
|
|
@@ -855,24 +854,46 @@ function Peer(id, options) {
|
|
throw new Error('API key can only contain alphanumerics, "_", and "-".');
|
|
throw new Error('API key can only contain alphanumerics, "_", and "-".');
|
|
}
|
|
}
|
|
|
|
|
|
- this.id = id;
|
|
|
|
- // Not used unless using cloud server.
|
|
|
|
- this._key = options.key;
|
|
|
|
-
|
|
|
|
- this._startSocket();
|
|
|
|
-
|
|
|
|
// Connections for this peer.
|
|
// Connections for this peer.
|
|
this.connections = {};
|
|
this.connections = {};
|
|
|
|
|
|
// Queued connections to make.
|
|
// Queued connections to make.
|
|
this._queued = [];
|
|
this._queued = [];
|
|
|
|
+
|
|
|
|
+ // Init immediately if ID is given, otherwise ask server for ID
|
|
|
|
+ if (id) {
|
|
|
|
+ this.id = id;
|
|
|
|
+ this._init();
|
|
|
|
+ } else {
|
|
|
|
+ this._getId();
|
|
|
|
+ }
|
|
};
|
|
};
|
|
|
|
|
|
util.inherits(Peer, EventEmitter);
|
|
util.inherits(Peer, EventEmitter);
|
|
|
|
|
|
-Peer.prototype._startSocket = function() {
|
|
|
|
|
|
+Peer.prototype._getId = function(cb) {
|
|
|
|
+ var self;
|
|
|
|
+ try {
|
|
|
|
+ var http = new XMLHttpRequest();
|
|
|
|
+ var url = 'http://' + this._options.host + ':' + this._options.port + '/' + this._options.key + '/id';
|
|
|
|
+ // 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) {
|
|
|
|
+ self.id = id;
|
|
|
|
+ self._init();
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ http.send(null);
|
|
|
|
+ } catch(e) {
|
|
|
|
+ this.emit('error', 'Could not get an ID from the server');
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Peer.prototype._init = function() {
|
|
var self = this;
|
|
var self = this;
|
|
- this._socket = new Socket(this._server, this.id, this._key);
|
|
|
|
|
|
+ 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);
|
|
});
|
|
});
|
|
@@ -1353,93 +1374,48 @@ DataConnection.prototype.handlePort = function(message) {
|
|
* An abstraction on top of WebSockets and XHR streaming to provide fastest
|
|
* An abstraction on top of WebSockets and XHR streaming to provide fastest
|
|
* possible connection for peers.
|
|
* possible connection for peers.
|
|
*/
|
|
*/
|
|
-function Socket(server, id, key) {
|
|
|
|
|
|
+function Socket(host, port, key, id) {
|
|
if (!(this instanceof Socket)) return new Socket(server, id, key);
|
|
if (!(this instanceof Socket)) return new Socket(server, id, key);
|
|
EventEmitter.call(this);
|
|
EventEmitter.call(this);
|
|
-
|
|
|
|
|
|
+
|
|
this._id = id;
|
|
this._id = id;
|
|
- this._server = server;
|
|
|
|
- this._httpUrl = 'http://' + this._server;
|
|
|
|
- this._key = key;
|
|
|
|
- this._token = util.randomToken();
|
|
|
|
|
|
+ var token = util.randomToken();
|
|
|
|
+
|
|
|
|
+ this._httpUrl = 'http://' + host + ':' + port + '/' + key + '/' + id + '/' + token;
|
|
|
|
+ this._wsUrl = 'ws://' + this._server + '/ws?key='+key+'id='+id+'token='+token;
|
|
|
|
+
|
|
|
|
+ this._index = 1;
|
|
};
|
|
};
|
|
|
|
|
|
util.inherits(Socket, EventEmitter);
|
|
util.inherits(Socket, EventEmitter);
|
|
|
|
|
|
|
|
+
|
|
/** Check in with ID or get one from server. */
|
|
/** Check in with ID or get one from server. */
|
|
-Socket.prototype._checkIn = function() {
|
|
|
|
- // If no ID provided, get a unique ID from server.
|
|
|
|
- var self = this;
|
|
|
|
- if (!this._id) {
|
|
|
|
- try {
|
|
|
|
- this._http = new XMLHttpRequest();
|
|
|
|
- var url = this._httpUrl;
|
|
|
|
- // Set API key if necessary.
|
|
|
|
- if (!!this._key) {
|
|
|
|
- url += '/' + this._key;
|
|
|
|
- }
|
|
|
|
- url += '/id?token=' + this._token;
|
|
|
|
-
|
|
|
|
- // If there's no ID we need to wait for one before trying to init socket.
|
|
|
|
- this._http.open('get', url, true);
|
|
|
|
- this._http.onreadystatechange = function() {
|
|
|
|
- if (!self._id && self._http.readyState > 2 && !!self._http.responseText) {
|
|
|
|
- try {
|
|
|
|
- var response = JSON.parse(self._http.responseText.split('\n').shift());
|
|
|
|
- if (!!response.id) {
|
|
|
|
- self._id = response.id;
|
|
|
|
- self._startWebSocket();
|
|
|
|
- self.emit('message', { type: 'OPEN', id: self._id });
|
|
|
|
- }
|
|
|
|
- } catch (e) {
|
|
|
|
- self._startWebSocket();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- self._handleStream(true);
|
|
|
|
- };
|
|
|
|
- this._http.send(null);
|
|
|
|
- this._setHTTPTimeout();
|
|
|
|
- } catch(e) {
|
|
|
|
- util.log('XMLHttpRequest not available; defaulting to WebSockets');
|
|
|
|
- this._startWebSocket();
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- this._startXhrStream();
|
|
|
|
- this._startWebSocket();
|
|
|
|
- }
|
|
|
|
|
|
+Socket.prototype.start = function() {
|
|
|
|
+ this._startXhrStream();
|
|
|
|
+ this._startWebSocket();
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Start up websocket communications. */
|
|
/** Start up websocket communications. */
|
|
Socket.prototype._startWebSocket = function() {
|
|
Socket.prototype._startWebSocket = function() {
|
|
|
|
+ var self = this;
|
|
|
|
+
|
|
if (!!this._socket) {
|
|
if (!!this._socket) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- var wsurl = 'ws://' + this._server + '/ws?';
|
|
|
|
- var query = ['token=' + this._token];
|
|
|
|
- if (!!this._id) {
|
|
|
|
- query.push('id=' + this._id);
|
|
|
|
- }
|
|
|
|
- if (!!this._key) {
|
|
|
|
- query.push('key=' + this._key);
|
|
|
|
- }
|
|
|
|
- wsurl += query.join('&');
|
|
|
|
this._socket = new WebSocket(wsurl);
|
|
this._socket = new WebSocket(wsurl);
|
|
-
|
|
|
|
- var self = this;
|
|
|
|
|
|
+
|
|
this._socket.onmessage = function(event) {
|
|
this._socket.onmessage = function(event) {
|
|
var data;
|
|
var data;
|
|
try {
|
|
try {
|
|
data = JSON.parse(event.data);
|
|
data = JSON.parse(event.data);
|
|
} catch(e) {
|
|
} catch(e) {
|
|
- data = event.data;
|
|
|
|
- }
|
|
|
|
- if (data.constructor == Object) {
|
|
|
|
- self.emit('message', data);
|
|
|
|
- } else {
|
|
|
|
util.log('Invalid server message', event.data);
|
|
util.log('Invalid server message', event.data);
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
+ self.emit('message', data);
|
|
};
|
|
};
|
|
|
|
|
|
// Take care of the queue of connections if necessary and make sure Peer knows
|
|
// Take care of the queue of connections if necessary and make sure Peer knows
|
|
@@ -1450,11 +1426,7 @@ Socket.prototype._startWebSocket = function() {
|
|
self._http.abort();
|
|
self._http.abort();
|
|
self._http = null;
|
|
self._http = null;
|
|
}
|
|
}
|
|
-
|
|
|
|
util.log('Socket open');
|
|
util.log('Socket open');
|
|
- if (self._id) {
|
|
|
|
- self.emit('open');
|
|
|
|
- }
|
|
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
|
|
@@ -1463,22 +1435,19 @@ Socket.prototype._startWebSocket = function() {
|
|
Socket.prototype._startXhrStream = function() {
|
|
Socket.prototype._startXhrStream = function() {
|
|
try {
|
|
try {
|
|
var self = this;
|
|
var self = this;
|
|
-
|
|
|
|
this._http = new XMLHttpRequest();
|
|
this._http = new XMLHttpRequest();
|
|
- var url = this._httpUrl;
|
|
|
|
- // Set API key if necessary.
|
|
|
|
- if (!!this._key) {
|
|
|
|
- url += '/' + this._key;
|
|
|
|
- }
|
|
|
|
- url += '/id';
|
|
|
|
- this._http.open('post', url, true);
|
|
|
|
- this._http.setRequestHeader('Content-Type', 'application/json');
|
|
|
|
|
|
+ this._http.open('post', this._httpUrl + '/id', true);
|
|
this._http.onreadystatechange = function() {
|
|
this._http.onreadystatechange = function() {
|
|
- self._handleStream();
|
|
|
|
|
|
+ if (!!self._http.old) {
|
|
|
|
+ self._http.old.abort();
|
|
|
|
+ delete self._http.old;
|
|
|
|
+ }
|
|
|
|
+ if (self._http.readyState > 2 && self._http.status == 200 && !!self._http.responseText) {
|
|
|
|
+ self._handleStream();
|
|
|
|
+ }
|
|
};
|
|
};
|
|
- this._http.send(JSON.stringify({ id: this._id, token: this._token }));
|
|
|
|
|
|
+ this._http.send(null);
|
|
this._setHTTPTimeout();
|
|
this._setHTTPTimeout();
|
|
-
|
|
|
|
} catch(e) {
|
|
} catch(e) {
|
|
util.log('XMLHttpRequest not available; defaulting to WebSockets');
|
|
util.log('XMLHttpRequest not available; defaulting to WebSockets');
|
|
}
|
|
}
|
|
@@ -1486,80 +1455,49 @@ Socket.prototype._startXhrStream = function() {
|
|
|
|
|
|
|
|
|
|
/** Handles onreadystatechange response as a stream. */
|
|
/** Handles onreadystatechange response as a stream. */
|
|
-Socket.prototype._handleStream = function(pad) {
|
|
|
|
|
|
+Socket.prototype._handleStream = function() {
|
|
|
|
+ var self = this;
|
|
// 3 and 4 are loading/done state. All others are not relevant.
|
|
// 3 and 4 are loading/done state. All others are not relevant.
|
|
- if (this._http.readyState < 3) {
|
|
|
|
- return;
|
|
|
|
- } else if (this._http.readyState == 3 && this._http.status != 200) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (this._index === undefined) {
|
|
|
|
- this._index = pad ? 2 : 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (this._http.responseText === null) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
var message = this._http.responseText.split('\n')[this._index];
|
|
var message = this._http.responseText.split('\n')[this._index];
|
|
- if (!!message && this._http.readyState == 3) {
|
|
|
|
|
|
+ if (!!message) {
|
|
this._index += 1;
|
|
this._index += 1;
|
|
try {
|
|
try {
|
|
- this._handleHTTPErrors(JSON.parse(message));
|
|
|
|
|
|
+ message = JSON.parse(message);
|
|
} catch(e) {
|
|
} catch(e) {
|
|
util.log('Invalid server message', message);
|
|
util.log('Invalid server message', message);
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
- } else if (this._http.readyState == 4) {
|
|
|
|
- this._index = 1;
|
|
|
|
|
|
+ self.emit('message', message);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
Socket.prototype._setHTTPTimeout = function() {
|
|
Socket.prototype._setHTTPTimeout = function() {
|
|
|
|
+ var self = this;
|
|
this._timeout = setTimeout(function() {
|
|
this._timeout = setTimeout(function() {
|
|
- var temp_http = self._http;
|
|
|
|
|
|
+ var old = self._http;
|
|
if (!self._wsOpen()) {
|
|
if (!self._wsOpen()) {
|
|
|
|
+ self._index = 1;
|
|
self._startXhrStream();
|
|
self._startXhrStream();
|
|
|
|
+ self._http.old = old;
|
|
|
|
+ } else {
|
|
|
|
+ old.abort();
|
|
}
|
|
}
|
|
- temp_http.abort();
|
|
|
|
}, 30000);
|
|
}, 30000);
|
|
};
|
|
};
|
|
|
|
|
|
-Socket.prototype._handleHTTPErrors = function(message) {
|
|
|
|
- switch (message.type) {
|
|
|
|
- case 'HTTP-ERROR':
|
|
|
|
- util.log('XHR ended in error.');
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- this.emit('message', message);
|
|
|
|
- }
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-
|
|
|
|
/** Exposed send for DC & Peer. */
|
|
/** Exposed send for DC & Peer. */
|
|
Socket.prototype.send = function(data) {
|
|
Socket.prototype.send = function(data) {
|
|
- var type = data.type;
|
|
|
|
- var message;
|
|
|
|
- if (!type) {
|
|
|
|
|
|
+ if (!data.type) {
|
|
this.emit('error', 'Invalid message');
|
|
this.emit('error', 'Invalid message');
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ message = JSON.stringify(data);
|
|
|
|
+
|
|
if (this._wsOpen()) {
|
|
if (this._wsOpen()) {
|
|
- message = JSON.stringify(data);
|
|
|
|
this._socket.send(message);
|
|
this._socket.send(message);
|
|
} else {
|
|
} else {
|
|
- data['token'] = this._token;
|
|
|
|
- message = JSON.stringify(data);
|
|
|
|
- var self = this;
|
|
|
|
var http = new XMLHttpRequest();
|
|
var http = new XMLHttpRequest();
|
|
- var url = this._httpUrl;
|
|
|
|
-
|
|
|
|
- // Set API key if necessary.
|
|
|
|
- if (!!this._key) {
|
|
|
|
- url += '/' + this._key;
|
|
|
|
- }
|
|
|
|
- url += '/' + type.toLowerCase();
|
|
|
|
-
|
|
|
|
|
|
+ var url = this._httpUrl + '/' + type.toLowerCase();
|
|
http.open('post', url, true);
|
|
http.open('post', url, true);
|
|
http.setRequestHeader('Content-Type', 'application/json');
|
|
http.setRequestHeader('Content-Type', 'application/json');
|
|
http.send(message);
|
|
http.send(message);
|
|
@@ -1576,8 +1514,5 @@ Socket.prototype._wsOpen = function() {
|
|
return !!this._socket && this._socket.readyState == 1;
|
|
return !!this._socket && this._socket.readyState == 1;
|
|
};
|
|
};
|
|
|
|
|
|
-Socket.prototype.start = function() {
|
|
|
|
- this._checkIn();
|
|
|
|
-};
|
|
|
|
|
|
|
|
})(this);
|
|
})(this);
|