Skip to content

Commit

Permalink
Improvement to api using server for signals
Browse files Browse the repository at this point in the history
  • Loading branch information
abhishekgahlot committed Jul 5, 2017
1 parent 0d7e7e3 commit 85576f4
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 94 deletions.
125 changes: 78 additions & 47 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,26 @@ const EventEmitter = require('events').EventEmitter;

const crypto = require('./crypto');

/**
* Signal class that uses promise not event emitters and can be used standalone.
* getSignal and updateSignal lets you update and fetch data regarding the shared key.
*/
class Signal {
/**
* @param {string} url
* @param {string} grtcID
* @param {string} peerID
* @param {object} signalID
* url is base url of page.
* grtcID is collaborate param from url.
* paramID is changing peerID on refresh or every new grtc instance.
* signalID is peer signal used to traverse and connect P2P.
*/
constructor(url, grtcID, signalID) {
constructor(url, grtcID, peerID, signalID) {
let self = this;
self.url = url;
self.grtcID = grtcID;
self.peerID = peerID;
self.signalID = signalID;
}

Expand Down Expand Up @@ -57,7 +64,12 @@ class Signal {
updateSignal() {
let self = this;
return new Promise((resolve, reject) => {
$.get(self.url + '/set/' + self.grtcID + '/' + btoa(JSON.stringify(self.signalID)), (resp) => {
let data = {
peerID: self.peerID,
signal: self.signalID,
type: 'register'
};
$.get(self.url + '/set/' + self.grtcID + '/' + btoa(JSON.stringify(data)), (resp) => {
resolve(resp);
}).fail((e) => {
reject(e);
Expand Down Expand Up @@ -124,20 +136,17 @@ class TransportLayer {
class GRTC extends EventEmitter {
/**
* @param {string} uuid
* @param {boolean} joinee
* uuid is uniquely generated id for collaboration to happen
* joinee is true if initiator else false
*/
constructor(grtcID, url, joinee, reload, useTransport) {
constructor(grtcID, url, useTransport) {
super();
let self = this;
self.peer = null;
self.peerSignal = null;
self.signalInstance = null;
self.joinee = joinee;
self.reload = reload;
self.url = url;
self.grtcID = grtcID;
self.peerID = GRTC.uuid();
self.otherPeers = new Set();
self.listenSignalTimer = 0;
self.listenSignalCount = 0;
Expand Down Expand Up @@ -179,16 +188,29 @@ class GRTC extends EventEmitter {
return difference;
}

/**
* Check if you are initiator or not.
*/
isInitiator() {
let self = this;
let data = { peerID: self.peerID, type: 'initial' };
$.get(self.url + '/set/' + self.grtcID + '/' + btoa(JSON.stringify(data)), (resp) => {
self.emit('initiator', resp);
}).fail((e) => {
self.emit('initiator', false);
});
}

/**
* Listens for signals by initiator.
* Listens for signals after finding initiator.
*/
listenSignal() {
let self = this;
self.listenSignalRoutine();
self.listenSignalTimer = setInterval(() => {
self.listenSignalCount++;
self.listenSignalRoutine();
}, 5000);
}, 3000);
}

/**
Expand All @@ -197,10 +219,10 @@ class GRTC extends EventEmitter {
listenSignalRoutine() {
let self = this;
self.signalInstance.getSignal().then((resp) => {
self.setDifference(new Set(resp), self.otherPeers).forEach((signal) => {
if (signal !== JSON.stringify(self.peerSignal)) {
self.emit('peerFound', signal);
self.otherPeers.add(signal);
resp.forEach((peer) => {
if (peer.peerID !== self.peerID && !self.otherPeers.has(peer.peerID)) {
self.emit('peerFound', peer);
self.otherPeers.add(peer.peerID);
}
});
});
Expand Down Expand Up @@ -231,6 +253,8 @@ class GRTC extends EventEmitter {
self.send({'secretAckConfirmed': true});
} else if ('secretAckConfirmed' in parsedData) {
self.emit('transport');
} else {
self.emit('peerData', parsedData);
}
}

Expand All @@ -243,7 +267,7 @@ class GRTC extends EventEmitter {
let self = this;
return new Promise((resolve, reject) => {
self.peer = new Peer({
initiator: self.joinee === true,
initiator: self.initiator === true,
trickle: false
});

Expand All @@ -252,8 +276,8 @@ class GRTC extends EventEmitter {
resolve();
});

self.on('peerFound', (signal) => {
self.peer.signal(signal);
self.on('peerFound', (peer) => {
self.peer.signal(peer.signal);
});

self.peer.on('signal', (data) => {
Expand Down Expand Up @@ -293,7 +317,7 @@ class GRTC extends EventEmitter {
/**
* Send public key to initiator only.
*/
if (self.joinee == false) {
if (self.initiator == false) {
self.send(payload);
}
});
Expand Down Expand Up @@ -324,16 +348,19 @@ class GRTC extends EventEmitter {
*/
startTransportLayer() {
let self = this;
console.log('starting transport layer', self.sharedSecret);
self.transportLayer = new TransportLayer(self.sharedSecret);

/**
* Create new send API
* Create new send API.
*/
self.secureSend = function(data) {
let newData = self.transportLayer.encrypt(JSON.stringify(data));
self.send(newData);
}

/**
* Listen of data received which is encrypted.
*/
self.on('peerEncryptedData', (encrypted) => {
let decryptedData = self.transportLayer.decrypt(encrypted);
self.emit('peerSecureData', decryptedData);
Expand All @@ -345,40 +372,44 @@ class GRTC extends EventEmitter {
*/
init() {
let self = this;
self.isInitiator();

/**
* Shared secret is generated by initiator and passed to others.
* Server decides if you get to initiate or not.
* Because of persistance independent of peers.
*/
if (self.joinee) {
self.sharedSecret = GRTC.secret();
}

/**
* if not initiator start listening for signals.
*/
if (self.joinee == false) {
self.signalInstance = new Signal(self.url, self.grtcID, self.peerSignal);
self.listenSignal();
}

/**
* Will be resolved by initiator only.
*/
self.peerHandler().then(() => {
self.signalInstance = new Signal(self.url, self.grtcID, self.peerSignal);
self.signalInstance.updateSignal().then(() => {
self.on('initiator', (resp) => {
/**
* Shared secret is generated by initiator and passed to others.
* else if not initiator start listening for signals.
*/
self.initiator = resp.initiator;
if (self.initiator) {
self.sharedSecret = GRTC.secret();
} else {
self.signalInstance = new Signal(self.url, self.grtcID, self.peerID, self.peerSignal);
self.listenSignal();
})
}

/**
* Will be resolved after initiator is set only.
*/
self.peerHandler().then(() => {
self.signalInstance = new Signal(self.url, self.grtcID, self.peerID, self.peerSignal);
self.signalInstance.updateSignal().then((data) => {
self.listenSignal();
});
});
});

/**
* Use transport layer for more security.
* Default is false.
*/
if (self.useTransport) {
self.securityHandler();
self.on('transport', self.startTransportLayer);
}
// /**
// * Use transport layer for more security.
// * Default is false.
// */
// if (self.useTransport) {
// self.securityHandler();
// self.on('transport', self.startTransportLayer);
// }
}
}

Expand Down
73 changes: 58 additions & 15 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,73 @@ app.get('/editor', (req, res) => {
res.sendFile(path.join(__dirname + '/static/editor.html'));
});

app.get('/tinymce', (req, res) => {
res.sendFile(path.join(__dirname + '/static/tinymce.html'));
});

app.get('/set/:key/:val', (req, res) => {
let key = req.params.key;
let val = new Buffer(req.params.val, 'base64').toString('ascii');
let force = req.query.force;

let firstSet = new Set();
let keyValue = kv.getSet(key);
let val = [];
try {
val = JSON.parse(new Buffer(req.params.val, 'base64').toString('ascii'));
} catch(e) {}

let force = req.query.force;
let peerID = val.peerID;
let type = val.type;
let store = kv.get(key) || [];

/**
* if force query parameter or keyValue doesn't exist
* Add value to new set and update the kv store.
* else add to set you got and update kv.
* If key doesn't exist and its initial request return initiator.
*/
if (force || !keyValue) {
firstSet.add(val);
res.send(kv.updateSet(key, firstSet));
} else {
keyValue.add(val);
res.send(kv.updateSet(key, keyValue));
if (!store.length && type === 'initial') {
let data = {
peerID,
type,
initiator: true
}
kv.put(key, [data]);
res.send(data);
} else if (store.length && type === 'initial') {
let exists = false;
store.forEach((peer) => {
if (peer.peerID == peerID) {
res.send(peer);
exists = true;
}
});

if (exists) { return; }

let data = {
peerID,
type,
initiator: false
}

kv.get(key).push(data);
kv.put(key, kv.get(key));
res.send(data);
} else if (type === 'register') {
store.forEach((peer) => {
if (peer.peerID === peerID) {
peer.signal = val.signal;
}
});
kv.put(key, kv.get(key));
res.send(kv.get(key));
}
});

app.get('/remove/:key', (req, res) => {
let key = req.params.key;
let store = kv.get(key);
kv.put(key, []);
res.send(kv.get(key));
});

app.get('/get/:key', (req, res) => {
res.send(Array.from(kv.getSet(req.params.key)));
res.send(kv.get(req.params.key));
});

app.listen(3000, () => {
Expand Down
7 changes: 3 additions & 4 deletions static/editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ <h3 class="panel-title">Editor</h3>
var grtc = new GRTC(grtcID, window.location.origin, false);
}
var inserting = false;
grtc.on('transport', function(){
grtc.on('peerConnected', function(){
console.log('secret acknowledged confirmed');
editor.getSession().on('change', function(e) {
window.x = e;
if(!inserting) {
grtc.secureSend(e);
grtc.send(e);
console.log('changed', e);
}
});
Expand All @@ -55,9 +55,8 @@ <h3 class="panel-title">Editor</h3>
});
grtc.on('peerData', function(d){
console.log(d);
var y = JSON.parse(d);
inserting = true;
editor.getSession().getDocument().applyDeltas([y]);
editor.getSession().getDocument().applyDeltas([d]);
inserting = false;
});
});
Expand Down
Loading

0 comments on commit 85576f4

Please sign in to comment.