|
@@ -17,21 +17,6 @@ binaryFeatures.useArrayBufferView = !binaryFeatures.useBlobBuilder && (function(
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
})();
|
|
})();
|
|
-binaryFeatures.supportsBinaryWebsockets = (function(){
|
|
|
|
- try {
|
|
|
|
- var wstest = new WebSocket('ws://null');
|
|
|
|
- wstest.onerror = function(){};
|
|
|
|
- if (typeof(wstest.binaryType) !== "undefined") {
|
|
|
|
- return true;
|
|
|
|
- } else {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- wstest.close();
|
|
|
|
- wstest = null;
|
|
|
|
- } catch (e) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
-})();
|
|
|
|
|
|
|
|
exports.binaryFeatures = binaryFeatures;
|
|
exports.binaryFeatures = binaryFeatures;
|
|
exports.BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder || window.BlobBuilder;
|
|
exports.BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder || window.BlobBuilder;
|
|
@@ -604,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;
|
|
@@ -817,6 +802,9 @@ var util = {
|
|
byteArray[i] = binary.charCodeAt(i) & 0xff;
|
|
byteArray[i] = binary.charCodeAt(i) & 0xff;
|
|
}
|
|
}
|
|
return byteArray.buffer;
|
|
return byteArray.buffer;
|
|
|
|
+ },
|
|
|
|
+ randomToken: function () {
|
|
|
|
+ return Math.random().toString(36).substr(2);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
var RTCPeerConnection = null;
|
|
var RTCPeerConnection = null;
|
|
@@ -906,18 +894,19 @@ Peer.prototype._startSocket = function() {
|
|
Peer.prototype._handleServerJSONMessage = function(message) {
|
|
Peer.prototype._handleServerJSONMessage = function(message) {
|
|
var peer = message.src;
|
|
var peer = message.src;
|
|
var connection = this.connections[peer];
|
|
var connection = this.connections[peer];
|
|
|
|
+ payload = message.payload;
|
|
switch (message.type) {
|
|
switch (message.type) {
|
|
case 'OPEN':
|
|
case 'OPEN':
|
|
if (!this.id) {
|
|
if (!this.id) {
|
|
// If we're just now getting an ID then we may have a queue.
|
|
// If we're just now getting an ID then we may have a queue.
|
|
- this.id = message.id;
|
|
|
|
|
|
+ this.id = payload.id;
|
|
}
|
|
}
|
|
this.emit('open', this.id);
|
|
this.emit('open', this.id);
|
|
this._processQueue();
|
|
this._processQueue();
|
|
break;
|
|
break;
|
|
case 'ERROR':
|
|
case 'ERROR':
|
|
- this.emit('error', message.msg);
|
|
|
|
- util.log(message.msg);
|
|
|
|
|
|
+ this.emit('error', payload.msg);
|
|
|
|
+ util.log(payload.msg);
|
|
break;
|
|
break;
|
|
case 'ID-TAKEN':
|
|
case 'ID-TAKEN':
|
|
this.emit('error', 'ID `'+this.id+'` is taken');
|
|
this.emit('error', 'ID `'+this.id+'` is taken');
|
|
@@ -925,17 +914,17 @@ Peer.prototype._handleServerJSONMessage = function(message) {
|
|
break;
|
|
break;
|
|
case 'OFFER':
|
|
case 'OFFER':
|
|
var options = {
|
|
var options = {
|
|
- metadata: message.metadata,
|
|
|
|
- sdp: message.sdp,
|
|
|
|
|
|
+ metadata: payload.metadata,
|
|
|
|
+ sdp: payload.sdp,
|
|
config: this._options.config,
|
|
config: this._options.config,
|
|
};
|
|
};
|
|
var connection = new DataConnection(this.id, peer, this._socket, options);
|
|
var connection = new DataConnection(this.id, peer, this._socket, options);
|
|
this._attachConnectionListeners(connection);
|
|
this._attachConnectionListeners(connection);
|
|
this.connections[peer] = connection;
|
|
this.connections[peer] = connection;
|
|
- this.emit('connection', connection, message.metadata);
|
|
|
|
|
|
+ this.emit('connection', connection, payload.metadata);
|
|
break;
|
|
break;
|
|
case 'EXPIRE':
|
|
case 'EXPIRE':
|
|
- connection = this.connections[message.expired];
|
|
|
|
|
|
+ connection = this.connections[payload.expired];
|
|
if (connection) {
|
|
if (connection) {
|
|
connection.close();
|
|
connection.close();
|
|
connection.emit('Could not connect to peer ' + connection.peer);
|
|
connection.emit('Could not connect to peer ' + connection.peer);
|
|
@@ -943,12 +932,12 @@ Peer.prototype._handleServerJSONMessage = function(message) {
|
|
break;
|
|
break;
|
|
case 'ANSWER':
|
|
case 'ANSWER':
|
|
if (connection) {
|
|
if (connection) {
|
|
- connection.handleSDP(message);
|
|
|
|
|
|
+ connection.handleSDP(payload);
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case 'CANDIDATE':
|
|
case 'CANDIDATE':
|
|
if (connection) {
|
|
if (connection) {
|
|
- connection.handleCandidate(message);
|
|
|
|
|
|
+ connection.handleCandidate(payload);
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case 'LEAVE':
|
|
case 'LEAVE':
|
|
@@ -962,7 +951,7 @@ Peer.prototype._handleServerJSONMessage = function(message) {
|
|
break;
|
|
break;
|
|
case 'PORT':
|
|
case 'PORT':
|
|
//if (util.browserisms === 'Firefox') {
|
|
//if (util.browserisms === 'Firefox') {
|
|
- // connection.handlePort(message);
|
|
|
|
|
|
+ // connection.handlePort(payload);
|
|
// break;
|
|
// break;
|
|
//}
|
|
//}
|
|
default:
|
|
default:
|
|
@@ -1142,9 +1131,10 @@ DataConnection.prototype._setupIce = function() {
|
|
util.log('Received ICE candidates');
|
|
util.log('Received ICE candidates');
|
|
self._socket.send({
|
|
self._socket.send({
|
|
type: 'CANDIDATE',
|
|
type: 'CANDIDATE',
|
|
- candidate: evt.candidate,
|
|
|
|
- dst: self.peer,
|
|
|
|
- src: self.id
|
|
|
|
|
|
+ payload: {
|
|
|
|
+ candidate: evt.candidate
|
|
|
|
+ },
|
|
|
|
+ dst: self.peer
|
|
});
|
|
});
|
|
}
|
|
}
|
|
};
|
|
};
|
|
@@ -1207,10 +1197,11 @@ DataConnection.prototype._makeOffer = function() {
|
|
util.log('Set localDescription to offer');
|
|
util.log('Set localDescription to offer');
|
|
self._socket.send({
|
|
self._socket.send({
|
|
type: 'OFFER',
|
|
type: 'OFFER',
|
|
- sdp: offer,
|
|
|
|
- dst: self.peer,
|
|
|
|
- src: self.id,
|
|
|
|
- metadata: self.metadata
|
|
|
|
|
|
+ payload: {
|
|
|
|
+ sdp: offer,
|
|
|
|
+ metadata: self.metadata
|
|
|
|
+ },
|
|
|
|
+ dst: self.peer
|
|
});
|
|
});
|
|
}, function(err) {
|
|
}, function(err) {
|
|
self.emit('error', 'Failed to setLocalDescription');
|
|
self.emit('error', 'Failed to setLocalDescription');
|
|
@@ -1228,8 +1219,9 @@ DataConnection.prototype._makeAnswer = function() {
|
|
util.log('Set localDescription to answer');
|
|
util.log('Set localDescription to answer');
|
|
self._socket.send({
|
|
self._socket.send({
|
|
type: 'ANSWER',
|
|
type: 'ANSWER',
|
|
- src: self.id,
|
|
|
|
- sdp: answer,
|
|
|
|
|
|
+ payload: {
|
|
|
|
+ sdp: answer
|
|
|
|
+ },
|
|
dst: self.peer
|
|
dst: self.peer
|
|
});
|
|
});
|
|
}, function(err) {
|
|
}, function(err) {
|
|
@@ -1285,8 +1277,7 @@ DataConnection.prototype.close = function() {
|
|
if (this.open) {
|
|
if (this.open) {
|
|
this._socket.send({
|
|
this._socket.send({
|
|
type: 'LEAVE',
|
|
type: 'LEAVE',
|
|
- dst: self.peer,
|
|
|
|
- src: self.id,
|
|
|
|
|
|
+ dst: self.peer
|
|
});
|
|
});
|
|
}
|
|
}
|
|
this.open = false;
|
|
this.open = false;
|
|
@@ -1316,16 +1307,17 @@ DataConnection.prototype.handleSDP = function(message) {
|
|
this._pc.setRemoteDescription(sdp, function() {
|
|
this._pc.setRemoteDescription(sdp, function() {
|
|
util.log('Set remoteDescription: ' + message.type);
|
|
util.log('Set remoteDescription: ' + message.type);
|
|
// Firefoxism
|
|
// Firefoxism
|
|
- if (message.type === 'ANSWER' && util.browserisms === 'Firefox') {
|
|
|
|
|
|
+ /**if (message.type === 'ANSWER' && util.browserisms === 'Firefox') {
|
|
self._pc.connectDataConnection(self.localPort, self.remotePort);
|
|
self._pc.connectDataConnection(self.localPort, self.remotePort);
|
|
self._socket.send({
|
|
self._socket.send({
|
|
type: 'PORT',
|
|
type: 'PORT',
|
|
dst: self.peer,
|
|
dst: self.peer,
|
|
- src: self.id,
|
|
|
|
- remote: self.localPort,
|
|
|
|
- local: self.remotePort
|
|
|
|
|
|
+ payload: {
|
|
|
|
+ remote: self.localPort,
|
|
|
|
+ local: self.remotePort
|
|
|
|
+ }
|
|
});
|
|
});
|
|
- } else if (message.type === 'OFFER') {
|
|
|
|
|
|
+ } else*/ if (message.type === 'OFFER') {
|
|
self._makeAnswer();
|
|
self._makeAnswer();
|
|
}
|
|
}
|
|
}, function(err) {
|
|
}, function(err) {
|
|
@@ -1370,6 +1362,7 @@ function Socket(server, id, key) {
|
|
this._server = server;
|
|
this._server = server;
|
|
this._httpUrl = 'http://' + this._server;
|
|
this._httpUrl = 'http://' + this._server;
|
|
this._key = key;
|
|
this._key = key;
|
|
|
|
+ this._token = util.randomToken();
|
|
};
|
|
};
|
|
|
|
|
|
util.inherits(Socket, EventEmitter);
|
|
util.inherits(Socket, EventEmitter);
|
|
@@ -1380,20 +1373,20 @@ Socket.prototype._checkIn = function() {
|
|
var self = this;
|
|
var self = this;
|
|
if (!this._id) {
|
|
if (!this._id) {
|
|
try {
|
|
try {
|
|
- var http = new XMLHttpRequest();
|
|
|
|
|
|
+ this._http = new XMLHttpRequest();
|
|
var url = this._httpUrl;
|
|
var url = this._httpUrl;
|
|
// Set API key if necessary.
|
|
// Set API key if necessary.
|
|
if (!!this._key) {
|
|
if (!!this._key) {
|
|
url += '/' + this._key;
|
|
url += '/' + this._key;
|
|
}
|
|
}
|
|
- url += '/id';
|
|
|
|
|
|
+ url += '/id?token=' + this._token;
|
|
|
|
|
|
// If there's no ID we need to wait for one before trying to init socket.
|
|
// 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 (!self._id && http.readyState > 2 && !!http.responseText) {
|
|
|
|
|
|
+ this._http.open('get', url, true);
|
|
|
|
+ this._http.onreadystatechange = function() {
|
|
|
|
+ if (!self._id && self._http.readyState > 2 && !!self._http.responseText) {
|
|
try {
|
|
try {
|
|
- var response = JSON.parse(http.responseText.split('\n').shift());
|
|
|
|
|
|
+ var response = JSON.parse(self._http.responseText.split('\n').shift());
|
|
if (!!response.id) {
|
|
if (!!response.id) {
|
|
self._id = response.id;
|
|
self._id = response.id;
|
|
self._startWebSocket();
|
|
self._startWebSocket();
|
|
@@ -1403,9 +1396,10 @@ Socket.prototype._checkIn = function() {
|
|
self._startWebSocket();
|
|
self._startWebSocket();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- self._handleStream(http, true);
|
|
|
|
|
|
+ self._handleStream(true);
|
|
};
|
|
};
|
|
- http.send(null);
|
|
|
|
|
|
+ this._http.send(null);
|
|
|
|
+ this._setHTTPTimeout();
|
|
} catch(e) {
|
|
} catch(e) {
|
|
util.log('XMLHttpRequest not available; defaulting to WebSockets');
|
|
util.log('XMLHttpRequest not available; defaulting to WebSockets');
|
|
this._startWebSocket();
|
|
this._startWebSocket();
|
|
@@ -1423,15 +1417,15 @@ Socket.prototype._startWebSocket = function() {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- var wsurl = 'ws://' + this._server + '/ws';
|
|
|
|
|
|
+ var wsurl = 'ws://' + this._server + '/ws?';
|
|
|
|
+ var query = ['token=' + this._token];
|
|
if (!!this._id) {
|
|
if (!!this._id) {
|
|
- wsurl += '?id=' + this._id;
|
|
|
|
- if (!!this._key) {
|
|
|
|
- wsurl += '&key=' + this._key;
|
|
|
|
- }
|
|
|
|
- } else if (!!this._key) {
|
|
|
|
- wsurl += '?key=' + this._key;
|
|
|
|
|
|
+ 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;
|
|
var self = this;
|
|
@@ -1452,6 +1446,12 @@ Socket.prototype._startWebSocket = function() {
|
|
// 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
|
|
// socket is open.
|
|
// socket is open.
|
|
this._socket.onopen = function() {
|
|
this._socket.onopen = function() {
|
|
|
|
+ if (!!self._timeout) {
|
|
|
|
+ clearTimeout(self._timeout);
|
|
|
|
+ self._http.abort();
|
|
|
|
+ self._http = null;
|
|
|
|
+ }
|
|
|
|
+
|
|
util.log('Socket open');
|
|
util.log('Socket open');
|
|
if (self._id) {
|
|
if (self._id) {
|
|
self.emit('open');
|
|
self.emit('open');
|
|
@@ -1465,19 +1465,21 @@ Socket.prototype._startXhrStream = function() {
|
|
try {
|
|
try {
|
|
var self = this;
|
|
var self = this;
|
|
|
|
|
|
- var http = new XMLHttpRequest();
|
|
|
|
|
|
+ this._http = new XMLHttpRequest();
|
|
var url = this._httpUrl;
|
|
var url = this._httpUrl;
|
|
// Set API key if necessary.
|
|
// Set API key if necessary.
|
|
if (!!this._key) {
|
|
if (!!this._key) {
|
|
url += '/' + this._key;
|
|
url += '/' + this._key;
|
|
}
|
|
}
|
|
url += '/id';
|
|
url += '/id';
|
|
- http.open('post', url, true);
|
|
|
|
- http.setRequestHeader('Content-Type', 'application/json');
|
|
|
|
- http.onreadystatechange = function() {
|
|
|
|
- self._handleStream(http);
|
|
|
|
|
|
+ this._http.open('post', url, true);
|
|
|
|
+ this._http.setRequestHeader('Content-Type', 'application/json');
|
|
|
|
+ this._http.onreadystatechange = function() {
|
|
|
|
+ self._handleStream();
|
|
};
|
|
};
|
|
- http.send(JSON.stringify({ id: this._id }));
|
|
|
|
|
|
+ this._http.send(JSON.stringify({ id: this._id, token: this._token }));
|
|
|
|
+ this._setHTTPTimeout();
|
|
|
|
+
|
|
} catch(e) {
|
|
} catch(e) {
|
|
util.log('XMLHttpRequest not available; defaulting to WebSockets');
|
|
util.log('XMLHttpRequest not available; defaulting to WebSockets');
|
|
}
|
|
}
|
|
@@ -1485,11 +1487,11 @@ Socket.prototype._startXhrStream = function() {
|
|
|
|
|
|
|
|
|
|
/** Handles onreadystatechange response as a stream. */
|
|
/** Handles onreadystatechange response as a stream. */
|
|
-Socket.prototype._handleStream = function(http, pad) {
|
|
|
|
|
|
+Socket.prototype._handleStream = function(pad) {
|
|
// 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 (http.readyState < 3) {
|
|
|
|
|
|
+ if (this._http.readyState < 3) {
|
|
return;
|
|
return;
|
|
- } else if (http.readyState == 3 && http.status != 200) {
|
|
|
|
|
|
+ } else if (this._http.readyState == 3 && this._http.status != 200) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1497,39 +1499,37 @@ Socket.prototype._handleStream = function(http, pad) {
|
|
this._index = pad ? 2 : 1;
|
|
this._index = pad ? 2 : 1;
|
|
}
|
|
}
|
|
|
|
|
|
- if (http.responseText === null) {
|
|
|
|
|
|
+ if (this._http.responseText === null) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- var message = http.responseText.split('\n')[this._index];
|
|
|
|
- if (!!message && http.readyState == 3) {
|
|
|
|
|
|
+ var message = this._http.responseText.split('\n')[this._index];
|
|
|
|
+ if (!!message && this._http.readyState == 3) {
|
|
this._index += 1;
|
|
this._index += 1;
|
|
try {
|
|
try {
|
|
this._handleHTTPErrors(JSON.parse(message));
|
|
this._handleHTTPErrors(JSON.parse(message));
|
|
} catch(e) {
|
|
} catch(e) {
|
|
util.log('Invalid server message', message);
|
|
util.log('Invalid server message', message);
|
|
}
|
|
}
|
|
- } else if (http.readyState == 4) {
|
|
|
|
|
|
+ } else if (this._http.readyState == 4) {
|
|
this._index = 1;
|
|
this._index = 1;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+Socket.prototype._setHTTPTimeout = function() {
|
|
|
|
+ this._timeout = setTimeout(function() {
|
|
|
|
+ var temp_http = self._http;
|
|
|
|
+ if (!self._wsOpen()) {
|
|
|
|
+ self._startXhrStream();
|
|
|
|
+ }
|
|
|
|
+ temp_http.abort();
|
|
|
|
+ }, 30000);
|
|
|
|
+};
|
|
|
|
|
|
Socket.prototype._handleHTTPErrors = function(message) {
|
|
Socket.prototype._handleHTTPErrors = function(message) {
|
|
switch (message.type) {
|
|
switch (message.type) {
|
|
- // XHR stream closed by timeout.
|
|
|
|
- case 'HTTP-END':
|
|
|
|
- util.log('XHR stream timed out.');
|
|
|
|
- if (!!this._socket && this._socket.readyState != 1) {
|
|
|
|
- this._startXhrStream();
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- // XHR stream closed by socket connect.
|
|
|
|
- case 'HTTP-SOCKET':
|
|
|
|
- util.log('XHR stream closed, WebSocket connected.');
|
|
|
|
- break;
|
|
|
|
case 'HTTP-ERROR':
|
|
case 'HTTP-ERROR':
|
|
- util.log('XHR ended in error or the websocket connected first.');
|
|
|
|
|
|
+ util.log('XHR ended in error.');
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
this.emit('message', message);
|
|
this.emit('message', message);
|
|
@@ -1537,18 +1537,20 @@ Socket.prototype._handleHTTPErrors = function(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 type = data.type;
|
|
- message = JSON.stringify(data);
|
|
|
|
|
|
+ var message;
|
|
if (!type) {
|
|
if (!type) {
|
|
this.emit('error', 'Invalid message');
|
|
this.emit('error', 'Invalid message');
|
|
}
|
|
}
|
|
|
|
|
|
- if (!!this._socket && this._socket.readyState == 1) {
|
|
|
|
|
|
+ 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 self = this;
|
|
var http = new XMLHttpRequest();
|
|
var http = new XMLHttpRequest();
|
|
var url = this._httpUrl;
|
|
var url = this._httpUrl;
|
|
@@ -1566,11 +1568,15 @@ Socket.prototype.send = function(data) {
|
|
};
|
|
};
|
|
|
|
|
|
Socket.prototype.close = function() {
|
|
Socket.prototype.close = function() {
|
|
- if (!!this._socket && this._socket.readyState == 1) {
|
|
|
|
|
|
+ if (!!this._wsOpen()) {
|
|
this._socket.close();
|
|
this._socket.close();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+Socket.prototype._wsOpen = function() {
|
|
|
|
+ return !!this._socket && this._socket.readyState == 1;
|
|
|
|
+};
|
|
|
|
+
|
|
Socket.prototype.start = function() {
|
|
Socket.prototype.start = function() {
|
|
this._checkIn();
|
|
this._checkIn();
|
|
};
|
|
};
|