mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2025-04-20 15:06:15 -04:00
Decrease redundancy by changing the way the websocket fallback is included; Adding new env var SIGNALING_SERVER to host client files but use another server for signaling.
This commit is contained in:
parent
cb72edef20
commit
3439e7f6d4
62 changed files with 439 additions and 10101 deletions
|
@ -1,24 +1,3 @@
|
|||
window.URL = window.URL || window.webkitURL;
|
||||
window.isRtcSupported = !!(window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection);
|
||||
|
||||
if (!window.isRtcSupported) alert("WebRTC must be enabled for PairDrop to work");
|
||||
|
||||
window.hiddenProperty = 'hidden' in document
|
||||
? 'hidden'
|
||||
: 'webkitHidden' in document
|
||||
? 'webkitHidden'
|
||||
: 'mozHidden' in document
|
||||
? 'mozHidden'
|
||||
: null;
|
||||
|
||||
window.visibilityChangeEvent = 'visibilitychange' in document
|
||||
? 'visibilitychange'
|
||||
: 'webkitvisibilitychange' in document
|
||||
? 'webkitvisibilitychange'
|
||||
: 'mozvisibilitychange' in document
|
||||
? 'mozvisibilitychange'
|
||||
: null;
|
||||
|
||||
class ServerConnection {
|
||||
|
||||
constructor() {
|
||||
|
@ -44,7 +23,33 @@ class ServerConnection {
|
|||
Events.on('offline', _ => clearTimeout(this._reconnectTimer));
|
||||
Events.on('online', _ => this._connect());
|
||||
|
||||
this._connect();
|
||||
this._getConfig().then(() => this._connect());
|
||||
}
|
||||
|
||||
_getConfig() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'config', true);
|
||||
xhr.addEventListener("load", () => {
|
||||
if (xhr.status === 200) {
|
||||
// Config received
|
||||
let config = JSON.parse(xhr.responseText);
|
||||
this._config = config;
|
||||
Events.fire('config', config);
|
||||
resolve()
|
||||
} else if (xhr.status !== 200) {
|
||||
// Handle errors
|
||||
console.error('Error:', xhr.status, xhr.statusText);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
xhr.send();
|
||||
})
|
||||
}
|
||||
|
||||
_setWsConfig(wsConfig) {
|
||||
this._wsConfig = wsConfig;
|
||||
Events.fire('ws-config', wsConfig);
|
||||
}
|
||||
|
||||
_connect() {
|
||||
|
@ -111,16 +116,12 @@ class ServerConnection {
|
|||
this.send({ type: 'leave-public-room' });
|
||||
}
|
||||
|
||||
_setRtcConfig(config) {
|
||||
window.rtcConfig = config;
|
||||
}
|
||||
|
||||
_onMessage(msg) {
|
||||
msg = JSON.parse(msg);
|
||||
if (msg.type !== 'ping') console.log('WS receive:', msg);
|
||||
switch (msg.type) {
|
||||
case 'rtc-config':
|
||||
this._setRtcConfig(msg.config);
|
||||
case 'ws-config':
|
||||
this._setWsConfig(msg.wsConfig);
|
||||
break;
|
||||
case 'peers':
|
||||
this._onPeers(msg);
|
||||
|
@ -170,6 +171,25 @@ class ServerConnection {
|
|||
case 'public-room-left':
|
||||
Events.fire('public-room-left');
|
||||
break;
|
||||
case 'request':
|
||||
case 'header':
|
||||
case 'partition':
|
||||
case 'partition-received':
|
||||
case 'progress':
|
||||
case 'files-transfer-response':
|
||||
case 'file-transfer-complete':
|
||||
case 'message-transfer-complete':
|
||||
case 'text':
|
||||
case 'display-name-changed':
|
||||
case 'ws-chunk':
|
||||
// ws-fallback
|
||||
if (this._wsConfig.wsFallback) {
|
||||
Events.fire('ws-relay', JSON.stringify(msg));
|
||||
}
|
||||
else {
|
||||
console.log("WS receive: message type is for websocket fallback only but websocket fallback is not activated on this instance.")
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error('WS receive: unknown message type', msg);
|
||||
}
|
||||
|
@ -209,17 +229,24 @@ class ServerConnection {
|
|||
}
|
||||
|
||||
_endpoint() {
|
||||
// hack to detect if deployment or development environment
|
||||
const protocol = location.protocol.startsWith('https') ? 'wss' : 'ws';
|
||||
const webrtc = window.isRtcSupported ? '/webrtc' : '/fallback';
|
||||
let ws_url = new URL(protocol + '://' + location.host + location.pathname + 'server' + webrtc);
|
||||
// Check whether the instance specifies another signaling server otherwise use the current instance for signaling
|
||||
let wsServerDomain = this._config.signalingServer
|
||||
? this._config.signalingServer
|
||||
: location.host + location.pathname;
|
||||
|
||||
let wsUrl = new URL(protocol + '://' + wsServerDomain + 'server');
|
||||
|
||||
wsUrl.searchParams.append('webrtc_supported', window.isRtcSupported ? 'true' : 'false');
|
||||
|
||||
const peerId = sessionStorage.getItem('peer_id');
|
||||
const peerIdHash = sessionStorage.getItem('peer_id_hash');
|
||||
if (peerId && peerIdHash) {
|
||||
ws_url.searchParams.append('peer_id', peerId);
|
||||
ws_url.searchParams.append('peer_id_hash', peerIdHash);
|
||||
wsUrl.searchParams.append('peer_id', peerId);
|
||||
wsUrl.searchParams.append('peer_id_hash', peerIdHash);
|
||||
}
|
||||
return ws_url.toString();
|
||||
|
||||
return wsUrl.toString();
|
||||
}
|
||||
|
||||
_disconnect() {
|
||||
|
@ -298,6 +325,9 @@ class Peer {
|
|||
this._send(JSON.stringify(message));
|
||||
}
|
||||
|
||||
// Is overwritten in expanding classes
|
||||
_send(message) {}
|
||||
|
||||
sendDisplayName(displayName) {
|
||||
this.sendJSON({type: 'display-name-changed', displayName: displayName});
|
||||
}
|
||||
|
@ -314,14 +344,24 @@ class Peer {
|
|||
return this._roomIds['secret'];
|
||||
}
|
||||
|
||||
_regenerationOfPairSecretNeeded() {
|
||||
return this._getPairSecret() && this._getPairSecret().length !== 256
|
||||
}
|
||||
|
||||
_getRoomTypes() {
|
||||
return Object.keys(this._roomIds);
|
||||
}
|
||||
|
||||
_updateRoomIds(roomType, roomId) {
|
||||
const roomTypeIsSecret = roomType === "secret";
|
||||
const roomIdIsNotPairSecret = this._getPairSecret() !== roomId;
|
||||
|
||||
// if peer is another browser tab, peer is not identifiable with roomSecret as browser tabs share all roomSecrets
|
||||
// -> do not delete duplicates and do not regenerate room secrets
|
||||
if (!this._isSameBrowser() && roomType === "secret" && this._isPaired() && this._getPairSecret() !== roomId) {
|
||||
if (!this._isSameBrowser()
|
||||
&& roomTypeIsSecret
|
||||
&& this._isPaired()
|
||||
&& roomIdIsNotPairSecret) {
|
||||
// multiple roomSecrets with same peer -> delete old roomSecret
|
||||
PersistentStorage
|
||||
.deleteRoomSecret(this._getPairSecret())
|
||||
|
@ -332,8 +372,13 @@ class Peer {
|
|||
|
||||
this._roomIds[roomType] = roomId;
|
||||
|
||||
if (!this._isSameBrowser() && roomType === "secret" && this._isPaired() && this._getPairSecret().length !== 256 && this._isCaller) {
|
||||
// increase security by initiating the increase of the roomSecret length from 64 chars (<v1.7.0) to 256 chars (v1.7.0+)
|
||||
if (!this._isSameBrowser()
|
||||
&& roomTypeIsSecret
|
||||
&& this._isPaired()
|
||||
&& this._regenerationOfPairSecretNeeded()
|
||||
&& this._isCaller) {
|
||||
// increase security by initiating the increase of the roomSecret length
|
||||
// from 64 chars (<v1.7.0) to 256 chars (v1.7.0+)
|
||||
console.log('RoomSecret is regenerated to increase security')
|
||||
Events.fire('regenerate-room-secret', this._getPairSecret());
|
||||
}
|
||||
|
@ -698,9 +743,12 @@ class Peer {
|
|||
|
||||
class RTCPeer extends Peer {
|
||||
|
||||
constructor(serverConnection, isCaller, peerId, roomType, roomId) {
|
||||
constructor(serverConnection, isCaller, peerId, roomType, roomId, rtcConfig) {
|
||||
super(serverConnection, isCaller, peerId, roomType, roomId);
|
||||
|
||||
this.rtcSupported = true;
|
||||
this.rtcConfig = rtcConfig
|
||||
|
||||
if (!this._isCaller) return; // we will listen for a caller
|
||||
this._connect();
|
||||
}
|
||||
|
@ -717,7 +765,7 @@ class RTCPeer extends Peer {
|
|||
}
|
||||
|
||||
_openConnection() {
|
||||
this._conn = new RTCPeerConnection(window.rtcConfig);
|
||||
this._conn = new RTCPeerConnection(this.rtcConfig);
|
||||
this._conn.onicecandidate = e => this._onIceCandidate(e);
|
||||
this._conn.onicecandidateerror = e => this._onError(e);
|
||||
this._conn.onconnectionstatechange = _ => this._onConnectionStateChange();
|
||||
|
@ -910,6 +958,48 @@ class RTCPeer extends Peer {
|
|||
}
|
||||
}
|
||||
|
||||
class WSPeer extends Peer {
|
||||
|
||||
constructor(serverConnection, isCaller, peerId, roomType, roomId) {
|
||||
super(serverConnection, isCaller, peerId, roomType, roomId);
|
||||
|
||||
this.rtcSupported = false;
|
||||
|
||||
if (!this._isCaller) return; // we will listen for a caller
|
||||
this._sendSignal();
|
||||
}
|
||||
|
||||
_send(chunk) {
|
||||
this.sendJSON({
|
||||
type: 'ws-chunk',
|
||||
chunk: arrayBufferToBase64(chunk)
|
||||
});
|
||||
}
|
||||
|
||||
sendJSON(message) {
|
||||
message.to = this._peerId;
|
||||
message.roomType = this._getRoomTypes()[0];
|
||||
message.roomId = this._roomIds[this._getRoomTypes()[0]];
|
||||
this._server.send(message);
|
||||
}
|
||||
|
||||
_sendSignal(connected = false) {
|
||||
this.sendJSON({type: 'signal', connected: connected});
|
||||
}
|
||||
|
||||
onServerMessage(message) {
|
||||
this._peerId = message.sender.id;
|
||||
Events.fire('peer-connected', {peerId: message.sender.id, connectionHash: this.getConnectionHash()})
|
||||
if (message.connected) return;
|
||||
this._sendSignal(true);
|
||||
}
|
||||
|
||||
getConnectionHash() {
|
||||
// Todo: implement SubtleCrypto asymmetric encryption and create connectionHash from public keys
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
class PeersManager {
|
||||
|
||||
constructor(serverConnection) {
|
||||
|
@ -937,6 +1027,13 @@ class PeersManager {
|
|||
Events.on('self-display-name-changed', e => this._notifyPeersDisplayNameChanged(e.detail));
|
||||
Events.on('notify-peer-display-name-changed', e => this._notifyPeerDisplayNameChanged(e.detail));
|
||||
Events.on('auto-accept-updated', e => this._onAutoAcceptUpdated(e.detail.roomSecret, e.detail.autoAccept));
|
||||
Events.on('ws-disconnected', _ => this._onWsDisconnected());
|
||||
Events.on('ws-relay', e => this._onWsRelay(e.detail));
|
||||
Events.on('ws-config', e => this._onWsConfig(e.detail));
|
||||
}
|
||||
|
||||
_onWsConfig(wsConfig) {
|
||||
this._wsConfig = wsConfig;
|
||||
}
|
||||
|
||||
_onMessage(message) {
|
||||
|
@ -944,9 +1041,10 @@ class PeersManager {
|
|||
this.peers[peerId].onServerMessage(message);
|
||||
}
|
||||
|
||||
_refreshPeer(peer, roomType, roomId) {
|
||||
if (!peer) return false;
|
||||
_refreshPeer(peerId, roomType, roomId) {
|
||||
if (!this._peerExists(peerId)) return false;
|
||||
|
||||
const peer = this.peers[peerId];
|
||||
const roomTypesDiffer = Object.keys(peer._roomIds)[0] !== roomType;
|
||||
const roomIdsDiffer = peer._roomIds[roomType] !== roomId;
|
||||
|
||||
|
@ -964,26 +1062,42 @@ class PeersManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
_createOrRefreshPeer(isCaller, peerId, roomType, roomId) {
|
||||
const peer = this.peers[peerId];
|
||||
if (peer) {
|
||||
this._refreshPeer(peer, roomType, roomId);
|
||||
_createOrRefreshPeer(isCaller, peerId, roomType, roomId, rtcSupported) {
|
||||
if (this._peerExists(peerId)) {
|
||||
this._refreshPeer(peerId, roomType, roomId);
|
||||
return;
|
||||
}
|
||||
|
||||
this.peers[peerId] = new RTCPeer(this._server, isCaller, peerId, roomType, roomId);
|
||||
if (window.isRtcSupported && rtcSupported) {
|
||||
this.peers[peerId] = new RTCPeer(this._server, isCaller, peerId, roomType, roomId, this._wsConfig.rtcConfig);
|
||||
}
|
||||
else if (this._wsConfig.wsFallback) {
|
||||
this.peers[peerId] = new WSPeer(this._server, isCaller, peerId, roomType, roomId);
|
||||
}
|
||||
else {
|
||||
console.warn("Websocket fallback is not activated on this instance.\n" +
|
||||
"Activate WebRTC in this browser or ask the admin of this instance to activate the websocket fallback.")
|
||||
}
|
||||
}
|
||||
|
||||
_onPeerJoined(message) {
|
||||
this._createOrRefreshPeer(false, message.peer.id, message.roomType, message.roomId);
|
||||
this._createOrRefreshPeer(false, message.peer.id, message.roomType, message.roomId, message.peer.rtcSupported);
|
||||
}
|
||||
|
||||
_onPeers(message) {
|
||||
message.peers.forEach(peer => {
|
||||
this._createOrRefreshPeer(true, peer.id, message.roomType, message.roomId);
|
||||
this._createOrRefreshPeer(true, peer.id, message.roomType, message.roomId, peer.rtcSupported);
|
||||
})
|
||||
}
|
||||
|
||||
_onWsRelay(message) {
|
||||
if (!this._wsConfig.wsFallback) return;
|
||||
|
||||
const messageJSON = JSON.parse(message);
|
||||
if (messageJSON.type === 'ws-chunk') message = base64ToArrayBuffer(messageJSON.chunk);
|
||||
this.peers[messageJSON.sender.id]._onMessage(message);
|
||||
}
|
||||
|
||||
_onRespondToFileTransferRequest(detail) {
|
||||
this.peers[detail.to]._respondToFileTransferRequest(detail.accepted);
|
||||
}
|
||||
|
@ -1009,6 +1123,9 @@ class PeersManager {
|
|||
}
|
||||
|
||||
_onPeerLeft(message) {
|
||||
if (this._peerExists(message.peerId) && this._webRtcSupported(message.peerId)) {
|
||||
console.log('WSPeer left:', message.peerId);
|
||||
}
|
||||
if (message.disconnect === true) {
|
||||
// if user actively disconnected from PairDrop server, disconnect all peer to peer connections immediately
|
||||
this._disconnectOrRemoveRoomTypeByPeerId(message.peerId, message.roomType);
|
||||
|
@ -1029,6 +1146,24 @@ class PeersManager {
|
|||
this._notifyPeerDisplayNameChanged(peerId);
|
||||
}
|
||||
|
||||
_peerExists(peerId) {
|
||||
return !!this.peers[peerId];
|
||||
}
|
||||
|
||||
_webRtcSupported(peerId) {
|
||||
return this.peers[peerId].rtcSupported
|
||||
}
|
||||
|
||||
_onWsDisconnected() {
|
||||
if (!this._wsConfig || !this._wsConfig.wsFallback) return;
|
||||
|
||||
for (const peerId in this.peers) {
|
||||
if (!this._webRtcSupported(peerId)) {
|
||||
Events.fire('peer-disconnected', peerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onPeerDisconnected(peerId) {
|
||||
const peer = this.peers[peerId];
|
||||
delete this.peers[peerId];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue