Skip to content

Instantly share code, notes, and snippets.

@mdsohelmia
Created September 2, 2023 08:17
Show Gist options
  • Save mdsohelmia/db5a512ade21259580eea2b3fa6d1750 to your computer and use it in GitHub Desktop.
Save mdsohelmia/db5a512ade21259580eea2b3fa6d1750 to your computer and use it in GitHub Desktop.
rum.js
"use strict";
(function (window) {
// Insight API
var API_URL = 'https://rum-cdn.cdnmadeeasy.com';
// Get resources list from the API
var RESOURCES_URL = 'https://rum-cdn.cdnmadeeasy.com/providers';
// Resources list TTL in the LocalStorage(5 minutes)
var RESOURCES_TTL = 5 * 60 * 1000; // Resources list TTL in the LocalStorage(5 minutes)
var QUEUE_LIMIT = 10; // Defaults for repeats. Those could be overridden by settings from rum-cdn
var REPEAT_DELAY = 60 * 1000; // How often the script will measure CDN performance while user staying on the same page
var REPEAT_COUNT = 3; // How many times repeat
var _ref = function () {
/* minified */
}.toString().indexOf('minified') !== -1 ? {
logInfo: console.log,
logError: console.error
} : {
logInfo: function logInfo() { },
logError: function logError() { }
},
logInfo = _ref.logInfo,
logError = _ref.logError;
// eslint-disable-next-line no-unused-vars
var customMeta = window.rum ? window.rum.customMeta : undefined;
var clientKey = window.rum ? window.rum.key : undefined;
var clientToken = window.rum ? window.rum.token : undefined;
function formatTiming(value) {
return Math.max(0, Number(value.toFixed(2)));
}
function fetch(url) {
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
_ref2$cb = _ref2.cb,
cb = _ref2$cb === void 0 ? function () { } : _ref2$cb,
data = _ref2.data,
_ref2$method = _ref2.method,
method = _ref2$method === void 0 ? 'GET' : _ref2$method,
_ref2$timeout = _ref2.timeout,
timeout = _ref2$timeout === void 0 ? 3000 : _ref2$timeout;
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
return resolve(xhr);
};
xhr.ontimeout = function () {
reject('timeout');
};
xhr.onerror = function () {
var errorMsg = "Unknown error happen when fetching ".concat(url);
if (xhr.status !== 0) {
errorMsg = "Error happen when fetching ".concat(url, ". Status ").concat(xhr.status, ". Response: ").concat(xhr.statusText);
}
reject(new Error(errorMsg));
};
xhr.open(method, url); // needs to happen after .open() because IE
xhr.timeout = timeout;
cb(xhr);
xhr.send(data);
});
}
function fetchJs(url, cb) {
var script = document.createElement('script');
var loaded = false;
script.onreadystatechange = script.onload = function () {
if (!loaded) {
cb();
}
loaded = true;
};
script.setAttribute('src', url);
document.body.appendChild(script);
}
function fetchResource(resource) {
var url = "".concat(resource.cdnUrl, "?t=").concat(Date.now());
var domain = new URL(url).hostname;
var result = {
id: resource.id,
domain: domain,
online: navigator && navigator.onLine !== undefined ? Number(navigator.onLine) : -1
};
return fetch(url).then(function (xhr) {
var timing = performance.getEntriesByName(url)[0];
var dnsLookupTime = formatTiming(timing.domainLookupEnd - timing.domainLookupStart);
var tcpTime = formatTiming(timing.connectEnd - timing.connectStart); // In Firefox if connection was cached - secure connection time is calculated
// as a difference between the page is loaded and current request execution started
// which is equal to the time spent on the page. That's why we check if actual tcp connection time is calculated.
var sslTime = tcpTime > 0 ? formatTiming(timing.connectEnd - timing.secureConnectionStart) : 0;
if (xhr.status >= 200 && xhr.status < 300) {
// Add success response measurements
result = Object.assign({}, result, {
up: 1,
status: xhr.status,
time: Number((timing.responseEnd - timing.requestStart).toFixed(2)),
dnsLookupTime: dnsLookupTime,
tcpTime: tcpTime,
sslTime: sslTime,
headers: xhr.getAllResponseHeaders()
});
}
if (xhr.status >= 300 && xhr.status <= 500) {
// HTTP Redirect/Error
result = Object.assign({}, result, {
up: 0,
status: xhr.status,
dnsLookupTime: dnsLookupTime,
tcpTime: tcpTime,
sslTime: sslTime,
headers: xhr.getAllResponseHeaders()
});
}
return result;
}).catch(function () {
return Object.assign({}, result, {
up: 0,
status: 0
});
});
}
function fetchResourceList() {
var key = 'gotipath-rum-resources';
var resources = JSON.parse(window.localStorage.getItem(key));
if (resources && resources.expiry > Date.now()) {
if (resources.config) setConfig(resources.config);
return Promise.resolve(resources.data);
}
return fetch(RESOURCES_URL).then(function (xhr) {
return JSON.parse(xhr.response);
}).then(function (response) {
if (!response || !response.data || !response.data.length) {
throw new Error('Empty response.data');
}
// Store resources list in the LocalStorage
window.localStorage.setItem(key, JSON.stringify({
data: response.data,
config: response.config,
expiry: Date.now() + RESOURCES_TTL
}));
if (response.config) setConfig(response.config);
return response.data;
}).catch(function () {
// eslint-disable-next-line
return [
{
"id": 1,
"cdnUrl": "https://rum-monitoring.gotipath.com/500b-bench.jpg",
"p": 1
},
{
"id": 2,
"cdnUrl": "https://rum-monitoring.gcdn.co/500b-bench.jpg",
"p": 1
}
];
});
}
function setConfig(config) {
REPEAT_DELAY = config.interval ? parseInt(config.interval) * 1000 : REPEAT_DELAY;
if (config.repeats) {
REPEAT_COUNT = config.repeats === 'Infinite' ? Math.pow(2, 32) // 4294967296
:
parseInt(config.repeats);
}
} // eslint-disable-next-line no-unused-vars
function logSample(data) {
var oneIn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000;
data.clientVersion = '1';
if (Math.random() < 1 / oneIn) {
fetch("".concat(API_URL, "/debug/log"), {
data: JSON.stringify(data),
method: 'POST'
});
}
}
function pMapSeries(array, fn) {
return array.reduce(function (acc, value) {
return acc.then(function (array) {
return fn(value).then(function (result) {
return array.push(result), array;
});
});
}, Promise.resolve([]));
}
function isBatchSafe(batch) {
var tooSlow = 0;
var down = 0; // Calculate number of down and slow
batch.forEach(function (data) {
if (!data.up) {
down++;
} else if (data.time > 2000) {
tooSlow++;
}
}); // If all in batch down or slow then skip data
return down + tooSlow < batch.length;
}
function run() {
var numberOfRepeat = 0;
var processor = function processor() {
fetchResourceList().then(function (response) {
var metadata = {
ua: navigator.userAgent,
hostname: window.location.hostname,
client: clientKey
};
var results = [];
performance.clearResourceTimings();
var processBatch = function processBatch() {
var batch = results.splice(0, results.length); // If batch is safe to store do it, else just skip data
if (isBatchSafe(batch)) {
return storeResults(metadata, batch);
}
return batch;
};
var processFetchResults = function processFetchResults(data) {
// Skip data if time not between 3 and 3000 ms
if (data.time < 3 || data.time > 3000) {
return false;
}
if (results.push(data) >= QUEUE_LIMIT) {
return processBatch();
}
};
return pMapSeries(response, function (resource) {
return fetchResource(resource).then(processFetchResults);
}).then(processBatch);
}).then(logInfo, logError).then(function () {
numberOfRepeat += 1;
if (numberOfRepeat < REPEAT_COUNT) {
setTimeout(processor, REPEAT_DELAY);
}
});
}; // prettier-ignore
// eslint-disable-next-line comma-dangle
setTimeout(processor, 3000);
}
function storeResults(metadata, data) {
// @IDEA Remove return to make async data store and do not wait for it before making another test
return fetch("".concat(API_URL, "/rum/v1"), {
data: JSON.stringify({
metadata: metadata,
data: data
}),
method: 'POST'
}).catch(logError);
}
function onLoad(fn) {
if (document.readyState === 'complete') {
fn();
} else {
window.addEventListener('load', fn);
}
}
onLoad(function () {
var knownBots = ['bot', 'headless', 'google', 'baidu', 'bing', 'msn', 'duckduckbot', 'teoma', 'slurp', 'yandex', 'phantomjs', 'pingdom', 'ahrefsbot'];
var botString = knownBots.join('|');
var botTestRegex = new RegExp(botString, 'i');
console.log(botString);
var isProbablyBot = botTestRegex.test(window.navigator.userAgent);
console.log("isProbablyBot", isProbablyBot);
if (isProbablyBot) {
console.warn('PerfOps RUM not launching due to suspicious userAgent:', window.navigator.userAgent);
return;
}
if (typeof window.performance === 'undefined') {
return;
} // Clear performance API entries buffer when full
performance.onresourcetimingbufferfull = function () {
logInfo('clear performance resource buffer');
performance.clearResourceTimings();
};
if (typeof Promise === 'function') {
return run();
}
});
})(window);
async function generateHMAC(sharedSecret, data) {
const encoder = new TextEncoder();
const secretKeyBuffer = await crypto.subtle.importKey(
'raw',
encoder.encode(sharedSecret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const dataBuffer = encoder.encode(data);
const hmacBuffer = await crypto.subtle.sign('HMAC', secretKeyBuffer, dataBuffer);
const hmacArray = Array.from(new Uint8Array(hmacBuffer));
const hmacDigest = hmacArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
return hmacDigest;
}
var navigator_info = window.navigator;
console.log("navigator_info", navigator_info);
var screen_info = window.screen;
console.log("screen_info", screen_info);
var uid = navigator_info.mimeTypes.length;
uid += navigator_info.userAgent.replace(/\D+/g, '');
uid += navigator_info.plugins.length;
uid += screen_info.height || '';
uid += screen_info.width || '';
uid += screen_info.pixelDepth || '';
uid += navigator_info.hardwareConcurrency || '';
console.log("hardwareConcurrency", navigator_info.hardwareConcurrency);
uid += navigator_info.productSub || '';
uid += navigator_info.platform.length || '';
console.log(uid);
document.write(uid);
console.log(window.navigator.userAgent);
// function generateFingerprint() {
// var fingerprint = '';
// // User Agent
// fingerprint += navigator.userAgent;
// // Screen Resolution
// fingerprint += screen.width + 'x' + screen.height;
// // Installed Fonts
// var fonts = '';
// for (var i = 0; i < navigator.fonts.length; i++) {
// fonts += navigator.fonts[i].family + ',';
// }
// fingerprint += fonts;
// // Time Zone Offset
// fingerprint += new Date().getTimezoneOffset();
// // Other attributes you can include: language preferences, browser plugins, etc.
// return fingerprint;
// }
// // Example usage
// var fingerprint = generateFingerprint();
// console.log(fingerprint);
var os = [
{ name: 'Windows Phone', value: 'Windows Phone', version: 'OS' },
{ name: 'Windows', value: 'Win', version: 'NT' },
{ name: 'iPhone', value: 'iPhone', version: 'OS' },
{ name: 'iPad', value: 'iPad', version: 'OS' },
{ name: 'Kindle', value: 'Kindle', version: 'Silk' },
{ name: 'Android', value: 'Android', version: 'Android' },
{ name: 'BlackBerry', value: 'BlackBerry', version: '/' },
{ name: 'Macintosh', value: 'Mac', version: 'OS X' },
{ name: 'Linux', value: 'Linux', version: 'rv' },
{ name: 'Palm', value: 'Palm', version: 'PalmOS' },
{ name: 'Chrome OS', value: 'CrOS', version: 'CrOS' },
{ name: 'FreeBSD', value: 'FreeBSD', version: 'FreeBSD' },
{ name: 'OpenBSD', value: 'OpenBSD', version: 'OpenBSD' },
{ name: 'Solaris', value: 'Solaris', version: 'SunOS' },
{ name: 'IBM AIX', value: 'AIX', version: 'AIX' }
];
var browser = [
{ name: 'Google Chrome', value: 'Chrome', version: 'Chrome' },
{ name: 'Mozilla Firefox', value: 'Firefox', version: 'Firefox' },
{ name: 'Microsoft Edge', value: 'Edge', version: 'Edge' },
{ name: 'Safari', value: 'Safari', version: 'Version' },
{ name: 'Opera', value: 'Opera', version: 'Opera' },
{ name: 'Internet Explorer', value: 'Internet Explorer', version: 'MSIE' },
{ name: 'Brave', value: 'Brave', version: 'Brave' },
{ name: 'Vivaldi', value: 'Vivaldi', version: 'Vivaldi' },
{ name: 'UC Browser', value: 'UCBrowser', version: 'UCBrowser' },
{ name: 'Samsung Internet', value: 'SamsungBrowser', version: 'SamsungBrowser' },
{ name: 'Maxthon', value: 'Maxthon', version: 'Maxthon' },
{ name: 'Yandex Browser', value: 'YaBrowser', version: 'YaBrowser' },
{ name: 'Torch Browser', value: 'Torch', version: 'Torch' },
{ name: 'Pale Moon', value: 'PaleMoon', version: 'PaleMoon' },
{ name: 'SeaMonkey', value: 'SeaMonkey', version: 'SeaMonkey' }
];
var header = [
navigator.platform,
navigator.userAgent,
navigator.appVersion,
navigator.vendor,
window.opera
];
function matchItem(string, data) {
var i = 0,
j = 0,
html = '',
regex,
regexv,
match,
matches,
version;
for (i = 0; i < data.length; i += 1) {
regex = new RegExp(data[i].value, 'i');
match = regex.test(string);
if (match) {
regexv = new RegExp(data[i].version + '[- /:;]([\d._]+)', 'i');
matches = string.match(regexv);
version = '';
if (matches) { if (matches[1]) { matches = matches[1]; } }
if (matches) {
matches = matches.split(/[._]+/);
for (j = 0; j < matches.length; j += 1) {
if (j === 0) {
version += matches[j] + '.';
} else {
version += matches[j];
}
}
} else {
version = '0';
}
return {
name: data[i].name,
version: parseFloat(version)
};
}
}
return { name: 'unknown', version: 0 };
}
var agent = header.join(' ');
var os = this.matchItem(agent, os);
var browser = this.matchItem(agent, browser);
// Device Information
var deviceType = navigator.userAgent.match(/Mobi/i) ? 'Mobile' : 'Desktop';
var deviceBrand = navigator.vendor || 'Unknown';
var deviceName = navigator.platform || 'Unknown';
// Screen Information
var screenHeight = window.screen.height || 'Unknown';
var screenWidth = window.screen.width || 'Unknown';
var pixelDensity = window.devicePixelRatio || 'Unknown';
// GPU Information
var gpu = navigator.userAgent.match(/AppleWebKit|Chrome|Gecko/i) ? 'GPU available' : 'GPU not available';
// Browser Information
var browserName = navigator.userAgent.split(' ')[0];
var browserVersion = navigator.userAgent.match(/(?:Version|Chrome|Firefox|MSIE|Edge)\/([\d.]+)/)[1] || 'Unknown';
// OS Information
var osName = navigator.appVersion.match(/(Mac|Windows|Linux)/i)[0] || 'Unknown';
var osCodeName = navigator.appVersion.match(/(NT|Intel|PPC|Mac)/i)[0] || 'Unknown';
var osVersion = navigator.appVersion.match(/(NT|Intel|PPC|Mac).*?([\d._]+)/)[2] || 'Unknown';
// Touchscreen Capabilities
var isTouchscreen = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment