This repository has been archived by the owner on Apr 3, 2019. It is now read-only.
/
flow-metrics.js
66 lines (56 loc) · 1.91 KB
/
flow-metrics.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const crypto = require('crypto');
const SALT_SIZE = 16;
const SALT_STRING_LENGTH = SALT_SIZE * 2;
module.exports = {
/**
* Create flow data.
*
* @param key String
* @param userAgent String
* @returns {{flowBeginTime: number, flowId: string}}
*/
create (key, userAgent) {
const salt = crypto.randomBytes(SALT_SIZE).toString('hex');
return createFlowEventData(key, salt, Date.now());
},
/**
* Check whether a flowId is valid.
*
* @param key String
* @param flowId String
* @param flowBeginTime Number
* @param userAgent String
* @returns Boolean
*/
validate (key, flowId, flowBeginTime, userAgent) {
const salt = flowId.substr(0, SALT_STRING_LENGTH);
let expected = createFlowEventData(key, salt, flowBeginTime);
if (getFlowSignature(flowId) === getFlowSignature(expected.flowId)) {
return true;
}
// HACK: We're transitioning between flow id recipes so, just for one train,
// fall back to trying the old way if the preceding check failed.
expected = createFlowEventData(key, salt, flowBeginTime, userAgent);
return getFlowSignature(flowId) === getFlowSignature(expected.flowId);
}
};
function createFlowEventData (key, ...data) {
const [ salt, flowBeginTime ] = data;
data[1] = flowBeginTime.toString(16);
// Incorporate a hash of request metadata into the flow id,
// so that receiving servers can cross-check the metrics bundle.
const flowSignature = crypto.createHmac('sha256', key)
.update(data.join('\n'))
.digest('hex')
.substr(0, SALT_STRING_LENGTH);
return {
flowBeginTime,
flowId: salt + flowSignature
};
}
function getFlowSignature (flowId) {
return flowId.substr(SALT_STRING_LENGTH);
}