boomcatch
Version:
A standalone, node.js-based beacon receiver for boomerang.
197 lines (167 loc) • 5.08 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 require, module */
/*jshint camelcase:false */
;
var packageInfo = require('../../package.json'),
check = require('check-types'),
useragent = require('useragent'),
url = require('url'),
querystring = require('querystring');
module.exports = {
initialise: function (/*options*/) {
// Asynchronously update the user agent database.
useragent(true);
return map;
},
type: 'json'
};
function map (data, referer, userAgent) {
if (!data.nt_nav_st || !data.restiming) {
return '';
}
return JSON.stringify({
log: {
version: '1.2',
creator: {
name: packageInfo.name,
version: packageInfo.version
},
browser: getBrowser(userAgent),
// HACK: The title must be set by the client with BOOMR.addVar(),
// otherwise we fall back to using the page URL.
pages: getPages(data, data.title || referer),
entries: getEntries(data.restiming)
}
});
}
function getBrowser (userAgent) {
var browser = useragent.lookup(userAgent).toJSON();
return {
name: browser.family,
version: browser.major
};
}
function getPages (data, title) {
return [
{
startedDateTime: getTime(data.nt_nav_st),
id: '0',
title: title || '',
pageTimings: getPageTimings(data)
}
];
}
function getTime (unixTime) {
var time = new Date();
time.setTime(parseInt(unixTime));
return time.toISOString();
}
function getPageTimings (data) {
return {
onContentLoad: parseInt(data.nt_domcontloaded_st),
onLoad: parseInt(data.nt_load_st)
};
}
function getEntries (data) {
var result = [];
Object.keys(data).forEach(function (key) {
var datum, timings;
datum = data[key];
timings = getTimings(datum);
result.push({
pageref: '0',
startedDateTime: getTime(datum.rt_st),
time: Object.keys(timings).reduce(function (sum, name) {
if (name === 'ssl' || check.not.positive(timings[name])) {
return sum;
}
return sum + timings[name];
}, 0),
request: getRequest(datum),
response: getResponse(datum),
cache: getCache(datum),
timings: timings
});
});
return result;
}
function getRequest (data) {
return {
method: '',
url: data.rt_name,
httpVersion: '',
cookies: [],
headers: [],
queryString: getQueryString(data.rt_name),
headerSize: -1,
bodySize: -1
};
}
function getQueryString (urlString) {
var parameters = querystring.parse(url.parse(urlString).query);
return Object.keys(parameters).map(function (name) {
return {
name: name,
value: parameters[name]
};
});
}
function getResponse (data) {
return {
status: -1,
statusText: '',
httpVersion: '',
cookies: [],
headers: [],
content: getContent(data),
redirectURL: '',
headerSize: -1,
bodySize: -1
};
}
function getContent (/*data*/) {
return {
size: -1,
mimeType: ''
};
}
function getCache (/*data*/) {
return {};
}
function getTimings (data) {
return {
blocked: parseInt(data.rt_fet_st) - parseInt(data.rt_st),
dns: getOptionalDuration(data.rt_dns_st, data.rt_dns_end),
connect: getOptionalDuration(data.rt_con_st, data.rt_con_end),
// HACK: The resource timing API doesn't provide us with separate
// metrics for `send` and `wait`, so we're assigning all of
// the time to `send` and setting `wait` to zero.
send: getOptionalDuration(data.rt_req_st, data.rt_res_st),
wait: 0,
receive: getOptionalDuration(data.rt_res_st, data.rt_res_end),
ssl: getOptionalDuration(data.rt_scon_st, data.rt_con_end)
};
}
function getOptionalDuration (start, end) {
start = parseInt(start);
end = parseInt(end);
if (check.not.number(start) || check.not.number(end)) {
return -1;
}
return end - start;
}