boomcatch
Version:
A standalone, node.js-based beacon receiver for boomerang.
228 lines (186 loc) • 6.77 kB
JavaScript
// Copyright © 2014, 2015, 2016 Springer Nature
//
// This file is part of boomcatch.
//
// Boomcatch is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Boomcatch is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with boomcatch. If not, see <http://www.gnu.org/licenses/>.
/*globals module, require */
;
var check = require('check-types');
module.exports = normalise;
function normalise (data) {
return {
rt: normaliseRtData(data),
navtiming: normaliseNavtimingData(data),
restiming: normaliseRestimingData(data)
};
}
function normaliseRtData (data) {
/*jshint camelcase:false */
var start, timeToFirstByte, timeToLastByte, timeToLoad;
start = getOptionalDatum(data, 'rt.tstart');
timeToFirstByte = getOptionalDatum(data, 't_resp');
timeToLastByte = getOptionalSum(data, 't_resp', 't_page');
timeToLoad = getOptionalDatum(data, 't_done');
check.assert.maybe.positive(start);
check.assert.maybe.number(timeToFirstByte);
check.assert.not.negative(timeToFirstByte);
check.assert.maybe.positive(timeToLastByte);
check.assert.maybe.positive(timeToLoad);
if (check.positive(timeToFirstByte) || check.positive(timeToLastByte) || check.positive(timeToLoad)) {
return {
timestamps: {
start: start
},
events: {},
durations: {
firstbyte: timeToFirstByte,
lastbyte: timeToLastByte,
load: timeToLoad
},
url: data.r
};
}
}
function getOptionalDatum (data, key) {
if (data[key]) {
return parseFloat(data[key]);
}
}
function getOptionalSum (data, aKey, bKey) {
if (data[aKey] && data[bKey]) {
return parseInt(data[aKey]) + parseInt(data[bKey]);
}
}
var normalisationMaps = {
navtiming: {
timestamps: [
{ key: 'nt_nav_st', name: 'start' },
{ key: 'nt_fet_st', name: 'fetchStart' },
{ key: 'nt_ssl_st', name: 'sslStart', optional: true },
{ key: 'nt_req_st', name: 'requestStart' },
{ key: 'nt_domint', name: 'domInteractive' }
],
events: [
{ start: 'nt_unload_st', end: 'nt_unload_end', name: 'unload' },
{ start: 'nt_red_st', end: 'nt_red_end', name: 'redirect' },
{ start: 'nt_dns_st', end: 'nt_dns_end', name: 'dns' },
{ start: 'nt_con_st', end: 'nt_con_end', name: 'connect' },
{ start: 'nt_res_st', end: 'nt_res_end', name: 'response' },
{ start: 'nt_domloading', end: 'nt_domcomp', name: 'dom' },
{ start: 'nt_domcontloaded_st', end: 'nt_domcontloaded_end', name: 'domContent' },
{ start: 'nt_load_st', end: 'nt_load_end', name: 'load' }
],
durations: []
},
restiming: {
timestamps: [
{ key: 'rt_st', name: 'start' },
{ key: 'rt_fet_st', name: 'fetchStart' },
{ key: 'rt_scon_st', name: 'sslStart', optional: true },
{ key: 'rt_req_st', name: 'requestStart', optional: true }
],
events: [
{ start: 'rt_red_st', end: 'rt_red_end', name: 'redirect', optional: true },
{ start: 'rt_dns_st', end: 'rt_dns_end', name: 'dns', optional: true },
{ start: 'rt_con_st', end: 'rt_con_end', name: 'connect', optional: true },
{ start: 'rt_res_st', end: 'rt_res_end', name: 'response', optional: true }
],
durations: []
}
};
function normaliseNavtimingData (data) {
/*jshint camelcase:false */
var result = normaliseCategory(normalisationMaps.navtiming, data, 'nt_nav_st');
if (result) {
result.type = data.nt_nav_type;
}
return result;
}
function normaliseCategory (map, data, startKey) {
try {
return {
timestamps: normaliseTimestamps(map, data),
events: normaliseEvents(map, data),
durations: normaliseDurations(map, data, startKey)
};
} catch (e) {
}
}
function normaliseTimestamps (map, data) {
return map.timestamps.reduce(function (result, timestamp) {
var value, assert;
if (data[timestamp.key]) {
value = parseInt(data[timestamp.key]);
}
assert = timestamp.optional ? check.assert.maybe : check.assert;
assert.positive(value);
if (value) {
result[timestamp.name] = value;
}
return result;
}, {});
}
function normaliseEvents (map, data) {
return map.events.reduce(function (result, event) {
var start, end, assert;
if (data[event.start] && data[event.end]) {
start = parseFloat(data[event.start]);
end = parseFloat(data[event.end]);
}
assert = event.optional ? check.assert.maybe : check.assert;
assert.number(start);
check.assert.not.negative(start);
assert.number(end);
check.assert.not.negative(end);
if (check.number(start) && check.number(end)) {
result[event.name] = {
start: start,
end: end
};
}
return result;
}, {});
}
function normaliseDurations (map, data, startKey) {
var start = parseFloat(data[startKey]);
return map.durations.reduce(function (result, duration) {
var value, assert;
if (data[duration.end]) {
value = parseFloat(data[duration.end]) - start;
}
assert = duration.optional ? check.assert.maybe : check.assert;
assert.number(value);
check.assert.not.negative(value);
if (value) {
result[duration.name] = value;
}
return result;
}, {});
}
function normaliseRestimingData (data) {
/*jshint camelcase:false */
var result;
if (data.restiming) {
result = [];
Object.keys(data.restiming).forEach(function (key) {
var datum = normaliseCategory(normalisationMaps.restiming, data.restiming[key], 'rt_st');
if (datum) {
datum.name = data.restiming[key].rt_name;
datum.type = data.restiming[key].rt_in_type;
}
result.push(datum);
});
return result;
}
}