Merge pull request #289 from Mikunj/p2p-ping

Updated pinging logic.
pull/299/head
Beaudan Campbell-Brown 6 years ago committed by GitHub
commit 1e11a6527c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -89,11 +89,11 @@ can public keys. To test the P2P functionality on the same machine, however, req
that each client binds their message server to a different port. that each client binds their message server to a different port.
You can use the following command to start a client bound to a different port. You can use the following command to start a client bound to a different port.
``` ```
yarn start-multi yarn start-multi
``` ```
For more than 2 clients, you can setup additional storage profiles and switch For more than 2 clients, you can setup additional storage profiles and switch
between them using the `NODE_APP_INSTANCE` environment variable and specifying a between them using the `NODE_APP_INSTANCE` environment variable and specifying a
new localServerPort in the config. new localServerPort in the config.
@ -149,14 +149,15 @@ So you wanna make a pull request? Please observe the following guidelines.
the translations in the translations in
[Transifex](https://www.transifex.com/projects/p/signal-desktop). [Transifex](https://www.transifex.com/projects/p/signal-desktop).
--> -->
* First, make sure that your `yarn ready` run passes - it's very similar to what our * First, make sure that your `yarn ready` run passes - it's very similar to what our
Continuous Integration servers do to test the app. Continuous Integration servers do to test the app.
* Never use plain strings right in the source code - pull them from `messages.json`! * Never use plain strings right in the source code - pull them from `messages.json`!
You **only** need to modify the default locale You **only** need to modify the default locale
[`_locales/en/messages.json`](_locales/en/messages.json). [`_locales/en/messages.json`](_locales/en/messages.json).
<!-- TODO: <!-- TODO:
Other locales are generated automatically based on that file and then periodically Other locales are generated automatically based on that file and then periodically
uploaded to Transifex for translation. --> uploaded to Transifex for translation. -->
* [Rebase](https://nathanleclaire.com/blog/2014/09/14/dont-be-scared-of-git-rebase/) your * [Rebase](https://nathanleclaire.com/blog/2014/09/14/dont-be-scared-of-git-rebase/) your
changes on the latest `development` branch, resolving any conflicts. changes on the latest `development` branch, resolving any conflicts.
This ensures that your changes will merge cleanly when you open your PR. This ensures that your changes will merge cleanly when you open your PR.

@ -1,6 +1,7 @@
/* global setTimeout, clearTimeout */ /* global setTimeout, clearTimeout */
const EventEmitter = require('events'); const EventEmitter = require('events');
const { isEmpty } = require('lodash');
const offlinePingTime = 2 * 60 * 1000; // 2 minutes const offlinePingTime = 2 * 60 * 1000; // 2 minutes
@ -18,68 +19,59 @@ class LokiP2pAPI extends EventEmitter {
}); });
} }
updateContactP2pDetails(pubKey, address, port, isPing = false) { updateContactP2pDetails(pubKey, address, port, isP2PMessage = false) {
// Stagger the timers so the friends don't ping each other at the same time // Stagger the timers so the friends don't ping each other at the same time
const timerDuration = const timerDuration =
pubKey < this.ourKey pubKey < this.ourKey
? 60 * 1000 // 1 minute ? 60 * 1000 // 1 minute
: 2 * 60 * 1000; // 2 minutes : 2 * 60 * 1000; // 2 minutes
if (!this.contactP2pDetails[pubKey]) { // Get the current contact details
// We didn't have this contact's details // This will be empty if we don't have them
this.contactP2pDetails[pubKey] = { const baseDetails = { ...(this.contactP2pDetails[pubKey] || {}) };
address,
port, // Always set the new contact details
timerDuration, this.contactP2pDetails[pubKey] = {
pingTimer: null, address,
isOnline: false, port,
}; timerDuration,
if (isPing) { pingTimer: null,
this.setContactOnline(pubKey); isOnline: false,
return; };
}
// Try ping const contactExists = !isEmpty(baseDetails);
this.pingContact(pubKey); const { isOnline } = baseDetails;
return; const detailsChanged =
} baseDetails.address !== address || baseDetails.port !== port;
// We already had this contact's details // If we had the contact details
const baseDetails = { ...this.contactP2pDetails[pubKey] }; // And we got a P2P message
// And the contact was online
if (isPing) { // And the new details that we got matched the old
// Received a ping // Then we don't need to bother pinging
// Update details in case they are new and mark online if (contactExists && isP2PMessage && isOnline && !detailsChanged) {
this.contactP2pDetails[pubKey].address = address; // We also need to set the current contact details to show online
this.contactP2pDetails[pubKey].port = port; // because they get reset to `false` above
this.setContactOnline(pubKey); this.setContactOnline(pubKey);
return; return;
} }
// Received a storage broadcast message /*
if ( Ping the contact.
baseDetails.isOnline || This happens in the following scenarios:
baseDetails.address !== address || 1. We didn't have the contact, we need to ping them to let them know our details.
baseDetails.port !== port 2. isP2PMessage = false, so we assume the contact doesn't have our details.
) { 3. We had the contact marked as offline,
// Had the contact marked as online and details we had were the same we need to make sure that we can reach their server.
this.pingContact(pubKey); 4. The other contact details have changed,
return; we need to make sure that we can reach their new server.
} */
// Had the contact marked as offline or got new details
this.contactP2pDetails[pubKey].address = address;
this.contactP2pDetails[pubKey].port = port;
this.setContactOffline(pubKey);
this.pingContact(pubKey); this.pingContact(pubKey);
} }
getContactP2pDetails(pubKey) { getContactP2pDetails(pubKey) {
return this.contactP2pDetails[pubKey] || null; if (!this.contactP2pDetails[pubKey]) return null;
} return { ...this.contactP2pDetails[pubKey] };
isContactOnline(pubKey) {
const contactDetails = this.contactP2pDetails[pubKey];
return !!(contactDetails && contactDetails.isOnline);
} }
setContactOffline(pubKey) { setContactOffline(pubKey) {

@ -106,7 +106,10 @@ const rpc = (address, port, method, params, options = {}) => {
method: 'POST', method: 'POST',
...options, ...options,
body: JSON.stringify(body), body: JSON.stringify(body),
headers, headers: {
'Content-Type': 'application/json',
...headers,
},
}; };
return fetch(url, fetchOptions); return fetch(url, fetchOptions);

@ -6,87 +6,188 @@ describe('LokiP2pAPI', () => {
const usedAddress = 'anAddress'; const usedAddress = 'anAddress';
const usedPort = 'aPort'; const usedPort = 'aPort';
const usedDetails = {
address: usedAddress,
port: usedPort,
timerDuration: 100,
pingTimer: null,
isOnline: false,
};
beforeEach(() => { beforeEach(() => {
this.lokiP2pAPI = new LokiP2pAPI(); this.lokiP2pAPI = new LokiP2pAPI();
}); });
afterEach(() => { afterEach(() => {
this.lokiP2pAPI.removeAllListeners();
this.lokiP2pAPI.reset(); this.lokiP2pAPI.reset();
}); });
it("Should not emit a pingContact event if that contact doesn't exits", () => { describe('getContactP2pDetails', () => {
this.lokiP2pAPI.on('pingContact', () => { it('Should return null if no contact details exist', () => {
assert.fail(); const details = this.lokiP2pAPI.getContactP2pDetails(usedKey);
assert.isNull(details);
}); });
this.lokiP2pAPI.pingContact('not stored');
});
it('Should emit an online event if the contact is online', done => { it('Should return the exact same object if contact details exist', () => {
this.lokiP2pAPI.on('online', pubKey => { this.lokiP2pAPI.contactP2pDetails[usedKey] = usedDetails;
assert.strictEqual(pubKey, usedKey); const details = this.lokiP2pAPI.getContactP2pDetails(usedKey);
done(); assert.deepEqual(details, usedDetails);
}); });
this.lokiP2pAPI.updateContactP2pDetails(
usedKey,
usedAddress,
usedPort,
true
);
}).timeout(1000);
it("Should send a pingContact event if the contact isn't online", done => {
this.lokiP2pAPI.on('pingContact', pubKey => {
assert.strictEqual(pubKey, usedKey);
done();
});
this.lokiP2pAPI.updateContactP2pDetails(
usedKey,
usedAddress,
usedPort,
false
);
}).timeout(1000);
it('Should store a contacts p2p details', () => {
this.lokiP2pAPI.updateContactP2pDetails(
usedKey,
usedAddress,
usedPort,
true
);
const p2pDetails = this.lokiP2pAPI.getContactP2pDetails(usedKey);
assert.strictEqual(usedAddress, p2pDetails.address);
assert.strictEqual(usedPort, p2pDetails.port);
}); });
it('Should say if a contact is online', () => { describe('pingContact', () => {
this.lokiP2pAPI.updateContactP2pDetails( it("Should not emit a pingContact event if that contact doesn't exits", () => {
usedKey, this.lokiP2pAPI.on('pingContact', () => {
usedAddress, assert.fail();
usedPort, });
false this.lokiP2pAPI.pingContact('not stored');
); });
assert.isFalse(this.lokiP2pAPI.isOnline(usedKey));
this.lokiP2pAPI.updateContactP2pDetails(
usedKey,
usedAddress,
usedPort,
true
);
assert.isTrue(this.lokiP2pAPI.isOnline(usedKey));
}); });
it('Should set a contact as offline', () => { describe('updateContactP2pDetails', () => {
this.lokiP2pAPI.updateContactP2pDetails( it("Shouldn't ping a contact if contact exists, p2p message was sent, contact was online and details didn't change", () => {
usedKey, this.lokiP2pAPI.on('pingContact', () => {
usedAddress, assert.fail();
usedPort, });
true
); // contact exists
let p2pDetails = this.lokiP2pAPI.getContactP2pDetails(usedKey); const details = { ...usedDetails };
assert.isTrue(p2pDetails.isOnline); // P2p message
p2pDetails = this.lokiP2pAPI.getContactP2pDetails(usedKey); const isP2P = true;
this.lokiP2pAPI.setContactOffline(usedKey); // Contact was online
assert.isFalse(p2pDetails.isOnline); details.isOnline = true;
// details were the same
const { address, port } = details;
this.lokiP2pAPI.contactP2pDetails[usedKey] = details;
this.lokiP2pAPI.updateContactP2pDetails(usedKey, address, port, isP2P);
// They should also be marked as online
assert.isTrue(this.lokiP2pAPI.isOnline(usedKey));
});
it("Should ping a contact if we don't have details for it", done => {
this.lokiP2pAPI.on('pingContact', pubKey => {
assert.strictEqual(pubKey, usedKey);
assert.isFalse(this.lokiP2pAPI.isOnline(usedKey));
done();
});
this.lokiP2pAPI.updateContactP2pDetails(
usedKey,
usedAddress,
usedPort,
true
);
});
it("Should ping a contact if a P2P message wasn't received", done => {
// The precondition for this is that we had the contact stored
this.lokiP2pAPI.contactP2pDetails[usedKey] = { ...usedDetails };
this.lokiP2pAPI.on('pingContact', pubKey => {
assert.strictEqual(pubKey, usedKey);
assert.isFalse(this.lokiP2pAPI.isOnline(usedKey));
done();
});
this.lokiP2pAPI.updateContactP2pDetails(
usedKey,
usedAddress,
usedPort,
false // We didn't get a p2p message
);
});
it('Should ping a contact if they were marked as offline', done => {
// The precondition for this is that we had the contact stored
// And that p2p message was true
this.lokiP2pAPI.contactP2pDetails[usedKey] = { ...usedDetails };
this.lokiP2pAPI.on('pingContact', pubKey => {
assert.strictEqual(pubKey, usedKey);
assert.isFalse(this.lokiP2pAPI.isOnline(usedKey));
done();
});
this.lokiP2pAPI.updateContactP2pDetails(
usedKey,
usedAddress,
usedPort,
true // We got a p2p message
);
});
it('Should ping a contact if the address was different', done => {
// The precondition for this is that we had the contact stored
// And that p2p message was true
// And that the user was online
this.lokiP2pAPI.contactP2pDetails[usedKey] = { ...usedDetails };
this.lokiP2pAPI.contactP2pDetails[usedKey].isOnline = true;
this.lokiP2pAPI.on('pingContact', pubKey => {
assert.strictEqual(pubKey, usedKey);
done();
});
this.lokiP2pAPI.updateContactP2pDetails(
usedKey,
'different address',
usedPort,
true // We got a p2p message
);
});
it('Should ping a contact if the port was different', done => {
// The precondition for this is that we had the contact stored
// And that p2p message was true
// And that the user was online
this.lokiP2pAPI.contactP2pDetails[usedKey] = { ...usedDetails };
this.lokiP2pAPI.contactP2pDetails[usedKey].isOnline = true;
this.lokiP2pAPI.on('pingContact', pubKey => {
assert.strictEqual(pubKey, usedKey);
done();
});
this.lokiP2pAPI.updateContactP2pDetails(
usedKey,
usedAddress,
'different port',
true // We got a p2p message
);
});
it('Should emit an online event if the contact is online', done => {
this.lokiP2pAPI.on('online', pubKey => {
assert.strictEqual(pubKey, usedKey);
done();
});
this.lokiP2pAPI.contactP2pDetails[usedKey] = { ...usedDetails };
this.lokiP2pAPI.setContactOnline(usedKey);
}).timeout(1000);
it('Should store a contacts p2p details', () => {
this.lokiP2pAPI.updateContactP2pDetails(
usedKey,
usedAddress,
usedPort,
true
);
const p2pDetails = this.lokiP2pAPI.getContactP2pDetails(usedKey);
assert.strictEqual(usedAddress, p2pDetails.address);
assert.strictEqual(usedPort, p2pDetails.port);
});
it('Should set a contact as offline and online', () => {
this.lokiP2pAPI.contactP2pDetails[usedKey] = { ...usedDetails };
let p2pDetails = this.lokiP2pAPI.getContactP2pDetails(usedKey);
assert.isNotNull(p2pDetails);
assert.isFalse(p2pDetails.isOnline);
this.lokiP2pAPI.setContactOnline(usedKey);
p2pDetails = this.lokiP2pAPI.getContactP2pDetails(usedKey);
assert.isTrue(p2pDetails.isOnline);
this.lokiP2pAPI.setContactOffline(usedKey);
p2pDetails = this.lokiP2pAPI.getContactP2pDetails(usedKey);
assert.isFalse(p2pDetails.isOnline);
});
}); });
}); });

Loading…
Cancel
Save