UNPKG

dynatrace-cordova-outsystems-plugin

Version:

This plugin gives you the ability to use the Dynatrace instrumentation in your hybrid application (Cordova, Ionic, ..). It uses the Mobile Agent, the JavaScript Agent and the Javascript Bridge. The Mobile Agent will give you all device specific values con

413 lines (280 loc) 9.76 kB
// Load modules var Dgram = require('dgram'); var Dns = require('dns'); var Hoek = require('hoek'); // Declare internals var internals = {}; exports.time = function (options, callback) { if (arguments.length !== 2) { callback = arguments[0]; options = {}; } var settings = Hoek.clone(options); settings.host = settings.host || 'pool.ntp.org'; settings.port = settings.port || 123; settings.resolveReference = settings.resolveReference || false; // Declare variables used by callback var timeoutId = 0; var sent = 0; // Ensure callback is only called once var finish = function (err, result) { if (timeoutId) { clearTimeout(timeoutId); timeoutId = 0; } socket.removeAllListeners(); socket.once('error', internals.ignore); socket.close(); return callback(err, result); }; finish = Hoek.once(finish); // Create UDP socket var socket = Dgram.createSocket('udp4'); socket.once('error', function (err) { return finish(err); }); // Listen to incoming messages socket.on('message', function (buffer, rinfo) { var received = Date.now(); var message = new internals.NtpMessage(buffer); if (!message.isValid) { return finish(new Error('Invalid server response'), message); } if (message.originateTimestamp !== sent) { return finish(new Error('Wrong originate timestamp'), message); } // Timestamp Name ID When Generated // ------------------------------------------------------------ // Originate Timestamp T1 time request sent by client // Receive Timestamp T2 time request received by server // Transmit Timestamp T3 time reply sent by server // Destination Timestamp T4 time reply received by client // // The roundtrip delay d and system clock offset t are defined as: // // d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2 var T1 = message.originateTimestamp; var T2 = message.receiveTimestamp; var T3 = message.transmitTimestamp; var T4 = received; message.d = (T4 - T1) - (T3 - T2); message.t = ((T2 - T1) + (T3 - T4)) / 2; message.receivedLocally = received; if (!settings.resolveReference || message.stratum !== 'secondary') { return finish(null, message); } // Resolve reference IP address Dns.reverse(message.referenceId, function (err, domains) { if (/* $lab:coverage:off$ */ !err /* $lab:coverage:on$ */) { message.referenceHost = domains[0]; } return finish(null, message); }); }); // Set timeout if (settings.timeout) { timeoutId = setTimeout(function () { timeoutId = 0; return finish(new Error('Timeout')); }, settings.timeout); } // Construct NTP message var message = new Buffer(48); for (var i = 0; i < 48; i++) { // Zero message message[i] = 0; } message[0] = (0 << 6) + (4 << 3) + (3 << 0) // Set version number to 4 and Mode to 3 (client) sent = Date.now(); internals.fromMsecs(sent, message, 40); // Set transmit timestamp (returns as originate) // Send NTP request socket.send(message, 0, message.length, settings.port, settings.host, function (err, bytes) { if (err || bytes !== 48) { return finish(err || new Error('Could not send entire message')); } }); }; internals.NtpMessage = function (buffer) { this.isValid = false; // Validate if (buffer.length !== 48) { return; } // Leap indicator var li = (buffer[0] >> 6); switch (li) { case 0: this.leapIndicator = 'no-warning'; break; case 1: this.leapIndicator = 'last-minute-61'; break; case 2: this.leapIndicator = 'last-minute-59'; break; case 3: this.leapIndicator = 'alarm'; break; } // Version var vn = ((buffer[0] & 0x38) >> 3); this.version = vn; // Mode var mode = (buffer[0] & 0x7); switch (mode) { case 1: this.mode = 'symmetric-active'; break; case 2: this.mode = 'symmetric-passive'; break; case 3: this.mode = 'client'; break; case 4: this.mode = 'server'; break; case 5: this.mode = 'broadcast'; break; case 0: case 6: case 7: this.mode = 'reserved'; break; } // Stratum var stratum = buffer[1]; if (stratum === 0) { this.stratum = 'death'; } else if (stratum === 1) { this.stratum = 'primary'; } else if (stratum <= 15) { this.stratum = 'secondary'; } else { this.stratum = 'reserved'; } // Poll interval (msec) this.pollInterval = Math.round(Math.pow(2, buffer[2])) * 1000; // Precision (msecs) this.precision = Math.pow(2, buffer[3]) * 1000; // Root delay (msecs) var rootDelay = 256 * (256 * (256 * buffer[4] + buffer[5]) + buffer[6]) + buffer[7]; this.rootDelay = 1000 * (rootDelay / 0x10000); // Root dispersion (msecs) this.rootDispersion = ((buffer[8] << 8) + buffer[9] + ((buffer[10] << 8) + buffer[11]) / Math.pow(2, 16)) * 1000; // Reference identifier this.referenceId = ''; switch (this.stratum) { case 'death': case 'primary': this.referenceId = String.fromCharCode(buffer[12]) + String.fromCharCode(buffer[13]) + String.fromCharCode(buffer[14]) + String.fromCharCode(buffer[15]); break; case 'secondary': this.referenceId = '' + buffer[12] + '.' + buffer[13] + '.' + buffer[14] + '.' + buffer[15]; break; } // Reference timestamp this.referenceTimestamp = internals.toMsecs(buffer, 16); // Originate timestamp this.originateTimestamp = internals.toMsecs(buffer, 24); // Receive timestamp this.receiveTimestamp = internals.toMsecs(buffer, 32); // Transmit timestamp this.transmitTimestamp = internals.toMsecs(buffer, 40); // Validate if (this.version === 4 && this.stratum !== 'reserved' && this.mode === 'server' && this.originateTimestamp && this.receiveTimestamp && this.transmitTimestamp) { this.isValid = true; } return this; }; internals.toMsecs = function (buffer, offset) { var seconds = 0; var fraction = 0; for (var i = 0; i < 4; ++i) { seconds = (seconds * 256) + buffer[offset + i]; } for (i = 4; i < 8; ++i) { fraction = (fraction * 256) + buffer[offset + i]; } return ((seconds - 2208988800 + (fraction / Math.pow(2, 32))) * 1000); }; internals.fromMsecs = function (ts, buffer, offset) { var seconds = Math.floor(ts / 1000) + 2208988800; var fraction = Math.round((ts % 1000) / 1000 * Math.pow(2, 32)); buffer[offset + 0] = (seconds & 0xFF000000) >> 24; buffer[offset + 1] = (seconds & 0x00FF0000) >> 16; buffer[offset + 2] = (seconds & 0x0000FF00) >> 8; buffer[offset + 3] = (seconds & 0x000000FF); buffer[offset + 4] = (fraction & 0xFF000000) >> 24; buffer[offset + 5] = (fraction & 0x00FF0000) >> 16; buffer[offset + 6] = (fraction & 0x0000FF00) >> 8; buffer[offset + 7] = (fraction & 0x000000FF); }; // Offset singleton internals.last = { offset: 0, expires: 0, host: '', port: 0 }; exports.offset = function (options, callback) { if (arguments.length !== 2) { callback = arguments[0]; options = {}; } var now = Date.now(); var clockSyncRefresh = options.clockSyncRefresh || 24 * 60 * 60 * 1000; // Daily if (internals.last.offset && internals.last.host === options.host && internals.last.port === options.port && now < internals.last.expires) { process.nextTick(function () { callback(null, internals.last.offset); }); return; } exports.time(options, function (err, time) { if (err) { return callback(err, 0); } internals.last = { offset: Math.round(time.t), expires: now + clockSyncRefresh, host: options.host, port: options.port }; return callback(null, internals.last.offset); }); }; // Now singleton internals.now = { intervalId: 0 }; exports.start = function (options, callback) { if (arguments.length !== 2) { callback = arguments[0]; options = {}; } if (internals.now.intervalId) { process.nextTick(function () { callback(); }); return; } exports.offset(options, function (err, offset) { internals.now.intervalId = setInterval(function () { exports.offset(options, function () { }); }, options.clockSyncRefresh || 24 * 60 * 60 * 1000); // Daily return callback(); }); }; exports.stop = function () { if (!internals.now.intervalId) { return; } clearInterval(internals.now.intervalId); internals.now.intervalId = 0; }; exports.isLive = function () { return !!internals.now.intervalId; }; exports.now = function () { var now = Date.now(); if (!exports.isLive() || now >= internals.last.expires) { return now; } return now + internals.last.offset; }; internals.ignore = function () { };