Skip to content

Commit

Permalink
feat(FEC-12850): ECDN Fallback Option Upon Inaccessible API Gateway (#…
Browse files Browse the repository at this point in the history
…201)

### Description of the Changes

add support for ECDN fallback upon inaccessible API gateway:

- make a ping request of the configured cdn to check if accessible
- the ping request should be executed only when there is accessControlActions configured with a regexAction
- we should apply the regex replacement if a ping was made and it was successful, or regexAction exists and there is no timeout configured
- add new configuration `provider.env.replaceHostOnlyManifestUrls` with default `false`, which indicates whether only playManifest urls should be applied with the regex; when configured to true- only play manifest urls should be replaced. if configured to false- in addition to manifest urls, apply the regex action to poster and captions urls

solves FEC-12850
  • Loading branch information
lianbenjamin authored Feb 2, 2023
1 parent 9592e13 commit fbcf62b
Show file tree
Hide file tree
Showing 13 changed files with 1,214 additions and 34 deletions.
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.*/node_modules/.*
[include]
[options]
esproposal.optional_chaining=enable
14 changes: 12 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ var provider = new playkit.providers.ott.Provider(config);
> {
> serviceUrl: string,
> cdnUrl: string,
> useApiCaptions: boolean
> useApiCaptions: boolean,
> replaceHostOnlyManifestUrls: boolean // optional
> }
> ```
>
Expand All @@ -110,7 +111,8 @@ var provider = new playkit.providers.ott.Provider(config);
> {
> serviceUrl: "//www.kaltura.com/api_v3",
> cdnUrl: "//cdnapisec.kaltura.com",
> useApiCaptions: true
> useApiCaptions: true,
> replaceHostOnlyManifestUrls: false
> }
> ```
>
Expand All @@ -132,6 +134,14 @@ var provider = new playkit.providers.ott.Provider(config);
> > ##### Default: true
> >
> > ##### Description: Show captions on platforms that don't support inband captions (for example: playing using Flash). This flag is for the OVP provider, and can be turned off by setting its value to `false`.
>
> > ### config.env.replaceHostOnlyManifestUrls
> >
> > ##### Type: `boolean`
> >
> > ##### Default: false
> >
> > ##### Description: Defines whether to replace host only for play manifest URLs or to replace also for captions and poster URLs. This flag is for OVP provider, and can be turned on by setting its value to `true`.
##
Expand Down
3 changes: 2 additions & 1 deletion flow-typed/types/env-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ declare type ProviderEnvConfigObject = {
serviceUrl: string,
cdnUrl?: string,
analyticsServiceUrl?: string,
useApiCaptions?: boolean
useApiCaptions?: boolean,
replaceHostOnlyManifestUrls?: boolean
};
3 changes: 2 additions & 1 deletion src/k-provider/ovp/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const defaultConfig: Object = {
format: 1
},
useApiCaptions: true,
loadThumbnailWithKs: false
loadThumbnailWithKs: false,
replaceHostOnlyManifestUrls: false
};

export default class OVPConfiguration {
Expand Down
24 changes: 2 additions & 22 deletions src/k-provider/ovp/provider-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ class OVPProviderParser {
OVPProviderParser._logger.warn(message);
return null;
}
mediaSource.url = OVPProviderParser._applyRegexAction(playbackContext, playUrl);
mediaSource.url = playUrl;
mediaSource.id = entryId + '_' + deliveryProfileId + ',' + format;
if (kalturaSource.hasDrmData()) {
const drmParams: Array<Drm> = [];
Expand Down Expand Up @@ -375,7 +375,7 @@ class OVPProviderParser {
OVPProviderParser._logger.warn(`failed to create play url from source, discarding source: (${entryId}_${deliveryProfileId}), ${format}.`);
return null;
} else {
mediaSource.url = OVPProviderParser._applyRegexAction(playbackContext, playUrl);
mediaSource.url = playUrl;
if (flavor.height && flavor.width) {
videoSources.push(mediaSource);
} else {
Expand Down Expand Up @@ -447,26 +447,6 @@ class OVPProviderParser {
static getErrorMessages(response: OVPMediaEntryLoaderResponse): Array<KalturaAccessControlMessage> {
return response.playBackContextResult.getErrorMessages();
}

/**
* Applies the request host regex on the url
* @function _applyRegexAction
* @param {KalturaPlaybackContext} playbackContext - The playback context
* @param {string} playUrl - The original url
* @returns {string} - The request host regex applied url
* @static
* @private
*/
static _applyRegexAction(playbackContext: KalturaPlaybackContext, playUrl: string): string {
const regexAction = playbackContext.getRequestHostRegexAction();
if (regexAction) {
const regex = new RegExp(regexAction.pattern, 'i');
if (playUrl.match(regex)) {
return playUrl.replace(regex, regexAction.replacement + '/');
}
}
return playUrl;
}
}

export const addKsToUrl = OVPProviderParser.addKsToUrl;
Expand Down
4 changes: 3 additions & 1 deletion src/k-provider/ovp/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import BaseProvider from '../common/base-provider';
import MediaEntry from '../../entities/media-entry';
import OVPEntryListLoader from './loaders/entry-list-loader';
import Error from '../../util/error/error';
import RegexActionHandler from './regex-action-handler';

export default class OVPProvider extends BaseProvider<OVPProviderMediaInfoObject> {
_filterOptionsConfig: ProviderFilterOptionsObject = {redirectFromEntryId: true};
Expand Down Expand Up @@ -59,7 +60,8 @@ export default class OVPProvider extends BaseProvider<OVPProviderMediaInfoObject
return this._dataLoader.fetchData().then(
response => {
try {
resolve(this._parseDataFromResponse(response));
const mediaConfig = this._parseDataFromResponse(response);
RegexActionHandler.handleRegexAction(mediaConfig, response).then(resolve);
} catch (err) {
reject(err);
}
Expand Down
154 changes: 154 additions & 0 deletions src/k-provider/ovp/regex-action-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//@flow
import getLogger from '../../util/logger';
import OVPConfiguration from './config';
import {KalturaAccessControlModifyRequestHostRegexAction} from './response-types';
import OVPMediaEntryLoader from './loaders/media-entry-loader';

class RegexActionHandler {
static _logger = getLogger('RegexActionHandler');

/**
* Applies the request host regex on the url
* @function _applyRegexAction
* @param {KalturaAccessControlModifyRequestHostRegexAction} regexAction - The regex action
* @param {string} url - The url to modify
* @returns {string} - The request host regex applied url
* @static
* @private
*/
static _applyRegexAction(regexAction: KalturaAccessControlModifyRequestHostRegexAction, url: string): string {
if (regexAction) {
const regex = new RegExp(regexAction.pattern, 'i');
if (url.match(regex)) {
return url.replace(regex, regexAction.replacement + '/');
}
}
return url;
}

/**
* Ping the ECDN url and replace the host urls if needed
* @function _pingECDNAndReplaceHostUrls
* @param {ProviderMediaConfigObject} mediaConfig - The media config
* @param {KalturaAccessControlModifyRequestHostRegexAction} regexAction - The regex action
* @param {string} cdnUrl - The CDN url
* @returns {Promise<ProviderMediaConfigObject>} - The media config with old or modified urls
* @static
* @private
*/
static _pingECDNAndReplaceHostUrls(
mediaConfig: ProviderMediaConfigObject,
regexAction: KalturaAccessControlModifyRequestHostRegexAction,
cdnUrl: string
): Promise<ProviderMediaConfigObject> {
return new Promise(resolve => {
const urlPing = cdnUrl + '/api_v3/service/system/action/ping/format/1';
const req = new XMLHttpRequest();
req.open('GET', urlPing);
req.timeout = regexAction.checkAliveTimeoutMs;
req.onreadystatechange = () => {
if (req.readyState === 4) {
if (req.status === 200) {
RegexActionHandler._replaceHostUrls(mediaConfig, regexAction);
}
resolve(mediaConfig);
}
};
req.ontimeout = () => {
RegexActionHandler._logger.warn(`Got timeout while pinging the ECDN url. the ping url: ${urlPing}`);
resolve(mediaConfig);
};
req.send();
});
}

/**
* Handles regex action
* @function handleRegexAction
* @param {ProviderMediaConfigObject} mediaConfig - The media config
* @param {Map<string, Function>} rawResponse - The raw response data from backend
* @returns {ProviderMediaConfigObject} - The media config with old or modified urls
* @static
*/
static handleRegexAction(mediaConfig: ProviderMediaConfigObject, rawResponse: Map<string, Function>): Promise<ProviderMediaConfigObject> {
return new Promise(resolve => {
let cdnUrl = OVPConfiguration.get().cdnUrl;
const regexAction = RegexActionHandler._extractRegexActionFromData(rawResponse);
const regExp = RegexActionHandler._getRegExp(regexAction);

if (!cdnUrl || !regexAction || !regExp || !cdnUrl.match(regExp)) {
RegexActionHandler._logger.debug('exiting handleRegexAction - not applying regex action.');
resolve(mediaConfig);
}

if (regexAction) {
cdnUrl = cdnUrl.replace(regExp, regexAction.replacement);
if (regexAction.checkAliveTimeoutMs > 0) {
RegexActionHandler._logger.debug('executing ping request...');
RegexActionHandler._pingECDNAndReplaceHostUrls(mediaConfig, regexAction, cdnUrl).then(resolve);
} else {
RegexActionHandler._replaceHostUrls(mediaConfig, regexAction);
resolve(mediaConfig);
}
}
});
}

/**
* Modify the host urls - injects the configured cdn before the original host, to route requests
* @function _replaceHostUrls
* @param {ProviderMediaConfigObject} mediaConfig - The media config
* @param {KalturaAccessControlModifyRequestHostRegexAction} regexAction - The regex action
* @returns {void}
* @static
* @private
*/
static _replaceHostUrls(mediaConfig: ProviderMediaConfigObject, regexAction: KalturaAccessControlModifyRequestHostRegexAction) {
RegexActionHandler._logger.debug(`Starting to modify urls...`);
const sources = mediaConfig.sources;
const {hls, dash, progressive, image} = sources;

[...hls, ...dash, ...progressive, ...image].forEach(src => (src.url = RegexActionHandler._applyRegexAction(regexAction, src.url)));

if (!OVPConfiguration.get().replaceHostOnlyManifestUrls) {
RegexActionHandler._logger.debug(`replaceHostOnlyManifestUrls flag is off - modifying captions and poster URLs`);
if (sources.captions) {
sources.captions.forEach(src => (src.url = RegexActionHandler._applyRegexAction(regexAction, src.url)));
}

// fix flow - poster can also be an array, but only for ott.
if (typeof sources.poster === 'string') {
sources.poster = RegexActionHandler._applyRegexAction(regexAction, sources.poster);
}
}
RegexActionHandler._logger.debug(`Finished modifying urls`);
}

/**
* Extracts the regex action from the data response
* @function _extractRegexActionFromData
* @param {Map<string, Function>} data - The response data
* @returns {KalturaAccessControlModifyRequestHostRegexAction} regexAction - The regex action
* @static
* @private
*/
static _extractRegexActionFromData(data: Map<string, Function>): ?KalturaAccessControlModifyRequestHostRegexAction {
return data.get(OVPMediaEntryLoader.id)?.response?.playBackContextResult.getRequestHostRegexAction();
}

/**
* Extracts the regex action from the data response
* @function _extractRegexActionFromData
* @param {KalturaAccessControlModifyRequestHostRegexAction} regexAction - The regex action
* @returns {RegExp} The regex expression
* @static
* @private
*/
static _getRegExp(regexAction: KalturaAccessControlModifyRequestHostRegexAction): ?RegExp {
if (regexAction && regexAction.pattern && regexAction.replacement) {
return new RegExp(regexAction.pattern, 'i');
}
}
}

export default RegexActionHandler;
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ export class KalturaAccessControlModifyRequestHostRegexAction extends KalturaRul
* @type {number}
*/
replacmenServerNodeId: number;

/**
* @member - checkAliveTimeout in milliseconds
* @type {number}
*/
checkAliveTimeoutMs: number;
/**
* @constructor
* @param {Object} data - The response
Expand All @@ -27,5 +31,6 @@ export class KalturaAccessControlModifyRequestHostRegexAction extends KalturaRul
this.pattern = data.pattern;
this.replacement = data.replacement;
this.replacmenServerNodeId = data.replacmenServerNodeId;
this.checkAliveTimeoutMs = data.checkAliveTimeoutMs;
}
}
13 changes: 13 additions & 0 deletions test/src/k-provider/ovp/be-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,19 @@ AnonymousMocEntryWithRequestHostRegexAction.response[2].actions = [
objectType: 'KalturaAccessControlModifyRequestHostRegexAction'
}
];
AnonymousMocEntryWithRequestHostRegexAction.response[2].playbackCaptions = [
{
label: 'English',
format: '2',
language: 'English',
webVttUrl:
'https://cfvod.kaltura.com/api_v3/index.php/service/caption_captionasset/action/serveWebVTT/captionAssetId/1_6jfbcdz9/segmentIndex/-1/version/11/captions.vtt',
url: 'https://cfvod.kaltura.com/api_v3/index.php/service/caption_captionAsset/action/serve/captionAssetId/1_6jfbcdz9/v/11',
isDefault: true,
languageCode: 'en',
objectType: 'KalturaCaptionPlaybackPluginData'
}
];

const AnonymousMocEntryWithoutUIConfWithDrmData = {
response: [
Expand Down
Loading

0 comments on commit fbcf62b

Please sign in to comment.