Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMCD: whitelist HTTP headers #3893

Merged
3 changes: 2 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ declare namespace dashjs {
cid?: string,
rtp?: number,
rtpSafetyFactor?: number,
mode?: 'query' | 'header'
mode?: 'query' | 'header',
enabledKeys?: Array<string>
}
};
errors?: {
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions samples/advanced/cmcd.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */
cid: '21cf726cfe3d937b5f974f72bb5bd06a', /* content id send with each request */
mode: CMCD_MODE_QUERY,
enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v']
}
}
});
Expand Down
60 changes: 48 additions & 12 deletions samples/dash-if-reference-player/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
$scope.audioTrackSwitchMode = 'neverReplace';
$scope.currentLogLevel = 'info';
$scope.cmcdMode = 'query';
$scope.cmcdAllKeys = ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v']

// Persistent license
$scope.persistentSessionId = {};
Expand Down Expand Up @@ -785,6 +786,35 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
});
}

$scope._getFormatedCmcdEnabledKeys = function () {
let formatedKeys;
if (!Array.isArray($scope.cmcdEnabledKeys)) {
let cmcdEnabledKeys = $scope.cmcdEnabledKeys.split(',');
formatedKeys = $scope.cmcdAllKeys.map(key => {
let mappedKey = key;
if (!cmcdEnabledKeys.includes(key)) mappedKey = '';

return mappedKey;
});
} else {
formatedKeys = $scope.cmcdEnabledKeys;
}

return formatedKeys
}

$scope.updateCmcdEnabledKeys = function () {
let cmcdEnabledKeys = $scope._getFormatedCmcdEnabledKeys();

$scope.player.updateSettings({
streaming: {
cmcd: {
enabledKeys: cmcdEnabledKeys
}
}
});
}

$scope.setStream = function (item) {
$scope.selectedItem = JSON.parse(JSON.stringify(item));
$scope.protData = {};
Expand Down Expand Up @@ -927,6 +957,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
config.streaming.cmcd.cid = $scope.cmcdContentId ? $scope.cmcdContentId : null;
config.streaming.cmcd.rtp = $scope.cmcdRtp ? $scope.cmcdRtp : null;
config.streaming.cmcd.rtpSafetyFactor = $scope.cmcdRtpSafetyFactor ? $scope.cmcdRtpSafetyFactor : null;
config.streaming.cmcd.enabledKeys = $scope.cmcdEnabledKeys ? $scope._getFormatedCmcdEnabledKeys() : [];

$scope.player.updateSettings(config);

Expand Down Expand Up @@ -1441,18 +1472,18 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
if (settings.hasOwnProperty(setting)) {
var k = prefix ? prefix + '.' + setting : setting,
v = settings[setting];
urlString.push((v != null && typeof v === "object") ?
urlString.push((v != null && typeof v === 'object') ?
this.toQueryString(v, k) :
encodeURIComponent(decodeURIComponent(k)) + "=" + encodeURIComponent(decodeURIComponent(v)));
encodeURIComponent(decodeURIComponent(k)) + '=' + encodeURIComponent(decodeURIComponent(v)));
}
}
// Make the string, then remove all cases of && caused by empty settings
return urlString.join("&").split('&&').join('&');
return urlString.join('&').split('&&').join('&');
}

/** Resolve nested query parameters */
$scope.resolveQueryNesting = function (base, nestedKey, value) {
var keyList = nestedKey.split(".");
var keyList = nestedKey.split('.');
var lastProperty = value !== null ? keyList.pop() : false;
var obj = base;

Expand All @@ -1474,15 +1505,15 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
$scope.toSettingsObject = function (queryString) {
//Remove double & in case of empty settings field
var querySegments = queryString.split('&&').join('&');
querySegments = queryString.split("&");
querySegments = queryString.split('&');
var settingsObject = {};
var drmObject = {};
var prioritiesEnabled = false;
var key, value;
var i = 1;

for (var segment in querySegments) {
[key, value] = querySegments[segment].split("=");
[key, value] = querySegments[segment].split('=');
value = decodeURIComponent(value);

$scope.resolveQueryNesting(settingsObject, key, value);
Expand Down Expand Up @@ -1545,22 +1576,22 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
queryProtectionData[drmObject[drm].drmKeySystem]['httpRequestHeaders'] = drmObject[drm].httpRequestHeaders;
}
} else {
alert("Kid and Key must be specified!");
alert('Kid and Key must be specified!');
}

} else {
//check if priority is enabled
if (prioritiesEnabled) {
queryProtectionData[drmObject[drm].drmKeySystem] = {
"serverURL": decodeURIComponent(drmObject[drm].licenseServerUrl),
"priority": parseInt(drmObject[drm].priority)
'serverURL': decodeURIComponent(drmObject[drm].licenseServerUrl),
'priority': parseInt(drmObject[drm].priority)
}
if (drmObject[drm].httpRequestHeaders !== {})
queryProtectionData[drmObject[drm].drmKeySystem]['httpRequestHeaders'] = drmObject[drm].httpRequestHeaders;

} else {
queryProtectionData[drmObject[drm].drmKeySystem] = {
"serverURL": decodeURIComponent(drmObject[drm].licenseServerUrl),
'serverURL': decodeURIComponent(drmObject[drm].licenseServerUrl),
}
}

Expand Down Expand Up @@ -1650,7 +1681,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
}

$scope.parseBoolean = function (value) {
return value === true || value === "true";
return value === true || value === 'true';
}

/** Takes a string value extracted from the query-string and transforms it into the appropriate type */
Expand Down Expand Up @@ -1678,7 +1709,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
//Alt2: If IE detected, copy settings-file content instead of creating a url, alert userto the change.
}
if (string.length > maxUrlLength) {
alert("The length of the URL may exceed the Browser url character limit.")
alert('The length of the URL may exceed the Browser url character limit.')
}
}

Expand Down Expand Up @@ -2032,7 +2063,12 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
if (currentConfig.streaming.cmcd.rtpSafetyFactor) {
$scope.cmcdRtpSafetyFactor = currentConfig.streaming.cmcd.rtpSafetyFactor;
}

$scope.cmcdMode = currentConfig.streaming.cmcd.mode;

if (currentConfig.streaming.cmcd.enabledKeys) {
$scope.cmcdEnabledKeys = currentConfig.streaming.cmcd.enabledKeys;
}
}

function getUrlVars() {
Expand Down
2 changes: 2 additions & 0 deletions samples/dash-if-reference-player/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,8 @@
ng-click="setCmcdMode()">
Header
</label>
<label class="options-label" data-toggle="tooltip" title="List of enabled cmcd keys for http headers">Enabled Header Keys</label>
<input type="text" class="form-control" placeholder="e.g. 'br,d,ot,...'" ng-model="cmcdEnabledKeys" ng-change="updateCmcdEnabledKeys()">
</div>
</div>

Expand Down
10 changes: 7 additions & 3 deletions src/core/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ import Events from './events/Events';
* cid: null,
* rtp: null,
* rtpSafetyFactor: 5,
* mode: Constants.CMCD_MODE_QUERY
* mode: Constants.CMCD_MODE_QUERY,
* enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v']
* }
* },
* errors: {
Expand Down Expand Up @@ -627,6 +628,8 @@ import Events from './events/Events';
* The method to use to attach cmcd metrics to the requests. 'query' to use query parameters, 'header' to use http headers.
*
* If not specified this value defaults to 'query'.
* @property {Array.<string>} [enabledKeys]
* This value is used to specify the desired CMCD parameters. Parameters not included in this list are not reported.
*/

/**
Expand Down Expand Up @@ -912,7 +915,8 @@ function Settings() {
cid: null,
rtp: null,
rtpSafetyFactor: 5,
mode: Constants.CMCD_MODE_QUERY
mode: Constants.CMCD_MODE_QUERY,
enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v']
}
},
errors: {
Expand All @@ -930,7 +934,7 @@ function Settings() {
for (let n in source) {
if (source.hasOwnProperty(n)) {
if (dest.hasOwnProperty(n)) {
if (typeof source[n] === 'object' && source[n] !== null) {
if (typeof source[n] === 'object' && !(source[n] instanceof Array) && source[n] !== null) {
mixinSettings(source[n], dest[n], path.slice() + n + '.');
} else {
dest[n] = Utils.clone(source[n]);
Expand Down
33 changes: 28 additions & 5 deletions src/streaming/models/CmcdModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ function CmcdModel() {
try {
if (settings.get().streaming.cmcd && settings.get().streaming.cmcd.enabled) {
const cmcdData = _getCmcdData(request);
const finalPayloadString = _buildFinalString(cmcdData);
const filteredCmcdData = _applyWhitelist(cmcdData);
const finalPayloadString = _buildFinalString(filteredCmcdData);

eventBus.trigger(MetricsReportingEvents.CMCD_DATA_GENERATED, {
url: request.url,
Expand All @@ -160,6 +161,22 @@ function CmcdModel() {
}
}

function _applyWhitelist(cmcdData) {
try {
const enabledCMCDKeys = settings.get().streaming.cmcd.enabledKeys;

return Object.keys(cmcdData)
.filter(key => enabledCMCDKeys.includes(key))
.reduce((obj, key) => {
obj[key] = cmcdData[key];

return obj;
}, {});
} catch (e) {
return cmcdData;
}
}

function _copyParameters(data, parameterNames) {
const copiedData = {};
for (let name of parameterNames) {
Expand All @@ -174,10 +191,10 @@ function CmcdModel() {
try {
if (settings.get().streaming.cmcd && settings.get().streaming.cmcd.enabled) {
const cmcdData = _getCmcdData(request);
const cmcdObjectHeader = _copyParameters(cmcdData, ['br', 'd', 'ot', 'tb']);
const cmcdRequestHeader = _copyParameters(cmcdData, ['bl', 'dl', 'mtp', 'nor', 'nrr', 'su']);
const cmcdStatusHeader = _copyParameters(cmcdData, ['bs', 'rtp']);
const cmcdSessionHeader = _copyParameters(cmcdData, ['cid', 'pr', 'sf', 'sid', 'st', 'v']);
const cmcdObjectHeader = _copyParameters(cmcdData, _applyWhitelistByKeys(['br', 'd', 'ot', 'tb']));
const cmcdRequestHeader = _copyParameters(cmcdData, _applyWhitelistByKeys(['bl', 'dl', 'mtp', 'nor', 'nrr', 'su']));
const cmcdStatusHeader = _copyParameters(cmcdData, _applyWhitelistByKeys(['bs', 'rtp']));
const cmcdSessionHeader = _copyParameters(cmcdData, _applyWhitelistByKeys(['cid', 'pr', 'sf', 'sid', 'st', 'v']));
const headers = {
'CMCD-Object': _buildFinalString(cmcdObjectHeader),
'CMCD-Request': _buildFinalString(cmcdRequestHeader),
Expand All @@ -200,6 +217,12 @@ function CmcdModel() {
}
}

function _applyWhitelistByKeys(keys) {
const enabledCMCDKeys = settings.get().streaming.cmcd.enabledKeys;

return keys.filter(key => enabledCMCDKeys.includes(key));
}

function _getCmcdData(request) {
try {
let cmcdData = null;
Expand Down
Loading