The communications platform that puts data protection first.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
Rocket.Chat/packages/meteor-timesync/timesync-client.js

175 lines
5.0 KiB

/* eslint-disable */
//IE8 doesn't have Date.now()
Date.now = Date.now || function() { return +new Date; };
TimeSync = {
loggingEnabled: true
};
function log(/* arguments */) {
if (TimeSync.loggingEnabled) {
Meteor._debug.apply(this, arguments);
}
}
var defaultInterval = 1000;
// Internal values, exported for testing
SyncInternals = {
offset: undefined,
roundTripTime: undefined,
offsetDep: new Deps.Dependency(),
timeTick: {},
timeCheck: function (lastTime, currentTime, interval, tolerance) {
if (Math.abs(currentTime - lastTime - interval) < tolerance) {
// Everything is A-OK
return true;
}
// We're no longer in sync.
return false;
}
};
SyncInternals.timeTick[defaultInterval] = new Deps.Dependency();
var maxAttempts = 5;
var attempts = 0;
/*
This is an approximation of
http://en.wikipedia.org/wiki/Network_Time_Protocol
If this turns out to be more accurate under the connect handlers,
we should try taking multiple measurements.
*/
// Only use Meteor.absoluteUrl for Cordova; see
// https://github.com/meteor/meteor/issues/4696
// https://github.com/mizzao/meteor-timesync/issues/30
var syncUrl = "/_timesync";
if (__meteor_runtime_config__.ROOT_URL_PATH_PREFIX) {
syncUrl = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX + syncUrl;
}
if (Meteor.isCordova) {
syncUrl = Meteor.absoluteUrl("_timesync");
}
var updateOffset = function() {
var t0 = Date.now();
HTTP.get(syncUrl, function(err, response) {
var t3 = Date.now(); // Grab this now
if (err) {
// We'll still use our last computed offset if is defined
log("Error syncing to server time: ", err);
if (++attempts <= maxAttempts)
Meteor.setTimeout(TimeSync.resync, 1000);
else
log("Max number of time sync attempts reached. Giving up.");
return;
}
attempts = 0; // It worked
var ts = parseInt(response.content);
SyncInternals.offset = Math.round(((ts - t0) + (ts - t3)) / 2);
SyncInternals.roundTripTime = t3 - t0; // - (ts - ts) which is 0
SyncInternals.offsetDep.changed();
});
};
// Reactive variable for server time that updates every second.
TimeSync.serverTime = function(clientTime, interval) {
check(interval, Match.Optional(Match.Integer));
// If we don't know the offset, we can't provide the server time.
if ( !TimeSync.isSynced() ) return undefined;
// If a client time is provided, we don't need to depend on the tick.
if ( !clientTime ) getTickDependency(interval || defaultInterval).depend();
// SyncInternals.offsetDep.depend(); implicit as we call isSynced()
// Convert Date argument to epoch as necessary
return (+clientTime || Date.now()) + SyncInternals.offset;
};
// Reactive variable for the difference between server and client time.
TimeSync.serverOffset = function() {
SyncInternals.offsetDep.depend();
return SyncInternals.offset;
};
TimeSync.roundTripTime = function() {
SyncInternals.offsetDep.depend();
return SyncInternals.roundTripTime;
};
TimeSync.isSynced = function() {
SyncInternals.offsetDep.depend();
return SyncInternals.offset !== undefined;
};
var resyncIntervalId = null;
TimeSync.resync = function() {
if (resyncIntervalId !== null) Meteor.clearInterval(resyncIntervalId);
updateOffset();
resyncIntervalId = Meteor.setInterval(updateOffset, 600000);
};
// Run this as soon as we load, even before Meteor.startup()
// Run again whenever we reconnect after losing connection
var wasConnected = false;
Deps.autorun(function() {
var connected = Meteor.status().connected;
if ( connected && !wasConnected ) TimeSync.resync();
wasConnected = connected;
});
// Resync if unexpected change by more than a few seconds. This needs to be
// somewhat lenient, or a CPU-intensive operation can trigger a re-sync even
// when the offset is still accurate. In any case, we're not going to be able to
// catch very small system-initiated NTP adjustments with this, anyway.
var tickCheckTolerance = 5000;
var lastClientTime = Date.now();
// Set up a new interval for any amount of reactivity.
function getTickDependency(interval) {
if ( !SyncInternals.timeTick[interval] ) {
var dep = new Deps.Dependency();
Meteor.setInterval(function() {
dep.changed();
}, interval);
SyncInternals.timeTick[interval] = dep;
}
return SyncInternals.timeTick[interval];
}
// Set up special interval for the default tick, which also watches for re-sync
Meteor.setInterval(function() {
var currentClientTime = Date.now();
if ( SyncInternals.timeCheck(
lastClientTime, currentClientTime, defaultInterval, tickCheckTolerance) ) {
// No problem here, just keep ticking along
SyncInternals.timeTick[defaultInterval].changed();
}
else {
// resync on major client clock changes
// based on http://stackoverflow.com/a/3367542/1656818
log("Clock discrepancy detected. Attempting re-sync.");
// Refuse to compute server time.
SyncInternals.offset = undefined;
SyncInternals.offsetDep.changed();
TimeSync.resync();
}
lastClientTime = currentClientTime;
}, defaultInterval);