Skip to content

Commit

Permalink
Add basic support for the Common Access Token (#4419)
Browse files Browse the repository at this point in the history
* Add basic support for the Common Access Token

* Add unit tests for CommonAccessTokenController
  • Loading branch information
dsilhavy committed Mar 15, 2024
1 parent 147a215 commit 9c64c5c
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 5 deletions.
6 changes: 6 additions & 0 deletions src/core/Utils.js
Expand Up @@ -180,6 +180,12 @@ class Utils {
}
}

static getHostFromUrl(urlString) {
const url = new URL(urlString);

return url.host
}

static parseUserAgent(ua = null) {
try {
const uaString = ua === null ? typeof navigator !== 'undefined' ? navigator.userAgent.toLowerCase() : '' : '';
Expand Down
3 changes: 2 additions & 1 deletion src/streaming/constants/Constants.js
Expand Up @@ -289,6 +289,7 @@ export default {
* @memberof Constants#
* @static
*/
ID3_SCHEME_ID_URI: 'https://aomedia.org/emsg/ID3'
ID3_SCHEME_ID_URI: 'https://aomedia.org/emsg/ID3',
COMMON_ACCESS_TOKEN_HEADER: 'common-access-token'
}

83 changes: 83 additions & 0 deletions src/streaming/controllers/CommonAccessTokenController.js
@@ -0,0 +1,83 @@
/**
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and contributor
* rights, including patent rights, and no such rights are granted under this license.
*
* Copyright (c) 2013, Dash Industry Forum.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * Neither the name of Dash Industry Forum nor the names of its
* contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

import FactoryMaker from '../../core/FactoryMaker.js';
import Constants from '../constants/Constants.js';
import Utils from '../../core/Utils.js';

function CommonAccessTokenController() {

let instance,
hostTokenMap;

function processResponseHeaders(httpResponse) {
if (!httpResponse || !httpResponse.headers || !httpResponse.request || !httpResponse.request.url) {
return
}

const commonAccessTokenHeader = httpResponse.headers[Constants.COMMON_ACCESS_TOKEN_HEADER]
const host = Utils.getHostFromUrl(httpResponse.request.url)
hostTokenMap[host] = commonAccessTokenHeader
}

function getCommonAccessTokenForUrl(url) {
if (!url) {
return null
}

const host = Utils.getHostFromUrl(url);
return hostTokenMap[host] ? hostTokenMap[host] : null;
}

function setup() {
_resetInitialSettings();
}

function reset() {
_resetInitialSettings()
}

function _resetInitialSettings() {
hostTokenMap = {}
}

instance = {
reset,
processResponseHeaders,
getCommonAccessTokenForUrl
};

setup();
return instance;
}

CommonAccessTokenController.__dashjs_factory_name = 'CommonAccessTokenController';
export default FactoryMaker.getSingletonFactory(CommonAccessTokenController);
31 changes: 27 additions & 4 deletions src/streaming/net/HTTPLoader.js
Expand Up @@ -42,6 +42,7 @@ import Events from '../../core/events/Events.js';
import Settings from '../../core/Settings.js';
import Constants from '../constants/Constants.js';
import CustomParametersModel from '../models/CustomParametersModel.js';
import CommonAccessTokenController from '../controllers/CommonAccessTokenController.js';

/**
* @module HTTPLoader
Expand Down Expand Up @@ -73,6 +74,7 @@ function HTTPLoader(cfg) {
xhrLoader,
fetchLoader,
customParametersModel,
commonAccessTokenController,
logger;

function setup() {
Expand All @@ -83,6 +85,7 @@ function HTTPLoader(cfg) {
cmcdModel = CmcdModel(context).getInstance();
cmsdModel = CmsdModel(context).getInstance();
customParametersModel = CustomParametersModel(context).getInstance();
commonAccessTokenController = CommonAccessTokenController(context).getInstance();

downloadErrorToRequestTypeMap = {
[HTTPRequest.MPD_TYPE]: errors.DOWNLOAD_ERROR_ID_MANIFEST_CODE,
Expand All @@ -95,6 +98,16 @@ function HTTPLoader(cfg) {
};
}

function setConfig(config) {
if (!config) {
return;
}

if (config.commonAccessTokenController) {
commonAccessTokenController = config.commonAccessTokenController
}
}

/**
* Initiates a download of the resource described by config.request.
* @param {Object} config - contains request (FragmentRequest or derived type), and callbacks
Expand Down Expand Up @@ -195,13 +208,15 @@ function HTTPLoader(cfg) {
}
};

const _oncomplete = function() {
const _oncomplete = function () {
// Update request timing info
requestObject.startDate = requestStartTime;
requestObject.endDate = new Date();
requestObject.firstByteDate = requestObject.firstByteDate || requestStartTime;
httpResponse.resourceTiming.responseEnd = requestObject.endDate.getTime();

commonAccessTokenController.processResponseHeaders(httpResponse)

// If enabled the ResourceTimingApi we add the corresponding information to the request object.
// These values are more accurate and can be used by the ThroughputController later
_addResourceTimingValues(httpRequest, httpResponse);
Expand Down Expand Up @@ -286,12 +301,13 @@ function HTTPLoader(cfg) {
eventBus.trigger(Events.ATTEMPT_BACKGROUND_SYNC);
}
}
} catch (e) {}
} catch (e) {
}

_retriggerRequest();
}

const _loadRequest = function(loader, httpRequest, httpResponse) {
const _loadRequest = function (loader, httpRequest, httpResponse) {
return new Promise((resolve) => {
_applyRequestInterceptors(httpRequest).then((_httpRequest) => {
httpRequest = _httpRequest;
Expand Down Expand Up @@ -580,6 +596,12 @@ function HTTPLoader(cfg) {
})
request.url = Utils.addAditionalQueryParameterToUrl(request.url, queryParams);
}

// Add headers from CommonAccessToken
const commonAccessToken = commonAccessTokenController.getCommonAccessTokenForUrl(request.url)
if (commonAccessToken) {
request.headers[Constants.COMMON_ACCESS_TOKEN_HEADER] = commonAccessToken
}
}

/**
Expand Down Expand Up @@ -644,7 +666,8 @@ function HTTPLoader(cfg) {

instance = {
load,
abort
abort,
setConfig
};

setup();
Expand Down
12 changes: 12 additions & 0 deletions test/unit/core.Utils.js
Expand Up @@ -109,4 +109,16 @@ describe('Utils', () => {
expect(Utils.stringHasProtocol('dash.akamaized.net')).to.be.false;
})
})

describe('getHostFromUrl', () => {

it('Should return a valid host for an http URL', () => {
expect(Utils.getHostFromUrl('http://dash.akamaized.net')).to.be.equal('dash.akamaized.net');
})

it('Should return a valid host for an http URL', () => {
expect(Utils.getHostFromUrl('https://dash.akamaized.net')).to.be.equal('dash.akamaized.net');
})

})
})
46 changes: 46 additions & 0 deletions test/unit/streaming.controllers.CommonAccessTokenController.js
@@ -0,0 +1,46 @@
import {expect} from 'chai';
import CommonAccessTokenController from '../../src/streaming/controllers/CommonAccessTokenController.js';
import Constants from '../../src/streaming/constants/Constants.js'

describe('CommonAccessTokenController', () => {

let commonAccessTokenController;

beforeEach(() => {
commonAccessTokenController = CommonAccessTokenController({}).getInstance();
})

it('getCommonAccessTokenForUrl should return null if no url is provided', () => {
expect(commonAccessTokenController.getCommonAccessTokenForUrl()).to.be.null
})

it('getCommonAccessTokenForUrl should return null if no value was added', () => {
const url = 'http://someurl.com'
expect(commonAccessTokenController.getCommonAccessTokenForUrl(url)).to.be.null
})

it('getCommonAccessTokenForUrl should return null if the added response does not contain the right header', () => {
const httpResponse = {
headers: {
'someheader': 'someheadervalue'
},
request: {
url: 'http://someurl.com'
}
}
commonAccessTokenController.processResponseHeaders(httpResponse);
expect(commonAccessTokenController.getCommonAccessTokenForUrl(httpResponse.request.url)).to.be.null
})

it('getCommonAccessTokenForUrl should return a CAT if the added response does contain the right header', () => {
const httpResponse = {
request: {
url: 'http://someurl.com'
}
}
httpResponse.headers = {};
httpResponse.headers[Constants.COMMON_ACCESS_TOKEN_HEADER] = 'cat';
commonAccessTokenController.processResponseHeaders(httpResponse);
expect(commonAccessTokenController.getCommonAccessTokenForUrl(httpResponse.request.url)).to.be.equal('cat')
})
})

0 comments on commit 9c64c5c

Please sign in to comment.