From 428c8923e9c0a9d4024e0e19f8080bc758192cf5 Mon Sep 17 00:00:00 2001 From: Robin Olsson Date: Thu, 1 Feb 2024 15:28:49 +0100 Subject: [PATCH 1/4] feat(#48): Added support for negative indexing when using rsq --- src/manifests/handlers/hls/media.ts | 4 +-- src/manifests/utils/configs.test.ts | 38 +++++++++++++++++++++++++ src/manifests/utils/configs.ts | 18 ++++++++++-- src/manifests/utils/hlsManifestUtils.ts | 14 +++------ 4 files changed, 59 insertions(+), 15 deletions(-) diff --git a/src/manifests/handlers/hls/media.ts b/src/manifests/handlers/hls/media.ts index a92bae4..2799211 100644 --- a/src/manifests/handlers/hls/media.ts +++ b/src/manifests/handlers/hls/media.ts @@ -78,12 +78,12 @@ export default async function hlsMediaHandler( } } } - const [error, allMutations, levelMutations] = configUtils.getAllManifestConfigs( mediaSequence, false, - mediaSequenceOffset + mediaSequenceOffset, + mediaM3U.items.PlaylistItem.length ); if (error) { return generateErrorResponse(error); diff --git a/src/manifests/utils/configs.test.ts b/src/manifests/utils/configs.test.ts index 2621aec..88b5841 100644 --- a/src/manifests/utils/configs.test.ts +++ b/src/manifests/utils/configs.test.ts @@ -193,8 +193,46 @@ describe('configs', () => { // Act const [err, actual] = configs.getAllManifestConfigs(0, false, 100); + // Assert + expect(err).toBeNull(); + expect(actual.get(115)).toEqual( + new Map([ + [ + 'statusCode', + { + fields: { code: 400 }, + sq: 115 + } + ] + ]) + ); + expect(actual.get(15)).toEqual( + new Map([ + [ + 'throttle', + { + fields: { rate: 1000 }, + sq: 15 + } + ] + ]) + ); + }); + it('should handle media sequence offsets with negative rsq value', () => { + // Arrange + const configs = statefulConfig.corruptorConfigUtils( + new URLSearchParams( + 'statusCode=[{rsq:-1,code:400}]&throttle=[{sq:15,rate:1000}]' + ) + ); + + configs.register(statusCodeConfig).register(throttleConfig); + + // Act + const [err, actual] = configs.getAllManifestConfigs(0, false, 100, 15); // Assert + console.log(actual); expect(err).toBeNull(); expect(actual.get(115)).toEqual( new Map([ diff --git a/src/manifests/utils/configs.ts b/src/manifests/utils/configs.ts index d3fd050..38bfc47 100644 --- a/src/manifests/utils/configs.ts +++ b/src/manifests/utils/configs.ts @@ -54,7 +54,8 @@ export interface CorruptorConfigUtils { getAllManifestConfigs: ( mseq?: number, isDash?: boolean, - mseqOffset?: number + mseqOffset?: number, + playlistSize?: number ) => [ ServiceError | null, IndexedCorruptorConfigMap | null, @@ -131,7 +132,12 @@ export const corruptorConfigUtils = function ( } return this; }, - getAllManifestConfigs(mseq = 0, isDash = false, mseqOffset = 0) { + getAllManifestConfigs( + mseq = 0, + isDash = false, + mseqOffset = 0, + playlistSize = 0 + ) { const outputMap = new CorruptorIndexMap(); const levelMap = new CorruptorLevelMap(); const configs = ( @@ -171,7 +177,12 @@ export const corruptorConfigUtils = function ( // Replace relative sequence numbers with absolute ones params = params.map((param) => { if (param.rsq) { - param.sq = Number(param.rsq) + mseqOffset; + const rsq = Number(param.rsq); + param['sq'] = + rsq < 0 && playlistSize > 0 + ? mseqOffset + playlistSize + rsq + 1 + : Number(param.rsq) + mseqOffset; + console.log('sq: ' + param.sq + ' | rsq: ' + Number(param.rsq)); delete param.rsq; } return param; @@ -182,6 +193,7 @@ export const corruptorConfigUtils = function ( if (error) { return [error, null]; } + configList.forEach((item) => { if (item.i != undefined) { outputMap.deepSet(item.i, config.name, item, false); diff --git a/src/manifests/utils/hlsManifestUtils.ts b/src/manifests/utils/hlsManifestUtils.ts index badc639..ff7e0de 100644 --- a/src/manifests/utils/hlsManifestUtils.ts +++ b/src/manifests/utils/hlsManifestUtils.ts @@ -1,9 +1,5 @@ import { M3U, Manifest } from '../../shared/types'; -import { - newState, - proxyPathBuilder, - segmentUrlParamString -} from '../../shared/utils'; +import { proxyPathBuilder, segmentUrlParamString } from '../../shared/utils'; import { CorruptorConfigMap, IndexedCorruptorConfigMap } from './configs'; import clone from 'clone'; @@ -143,15 +139,13 @@ export default function (): HLSManifestTools { configsMap: IndexedCorruptorConfigMap ) { const m3u: M3U = clone(originalM3U); + const playlistSize = m3u.items.PlaylistItem.length; // configs for each index - const corruptions = this.utils.mergeMap( - m3u.items.PlaylistItem.length, - configsMap - ); + const corruptions = this.utils.mergeMap(playlistSize, configsMap); // Attach corruptions to manifest - for (let i = 0; i < m3u.items.PlaylistItem.length; i++) { + for (let i = 0; i < playlistSize; i++) { const item = m3u.items.PlaylistItem[i]; const corruption = corruptions[i]; let sourceSegURL: string = item.get('uri'); From 595dadcb5e3af3f5b9f43c05d1b164428af0d558 Mon Sep 17 00:00:00 2001 From: Robin Olsson Date: Thu, 1 Feb 2024 16:26:47 +0100 Subject: [PATCH 2/4] fix: removed console.log and fixed wrong example url in readme --- README.md | 2 +- src/manifests/utils/configs.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index edb23fa..09b5aab 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ Currently, the Chaos Stream Proxy supports 4 types of corruptions for HLS and MP To specify the configurations for a particular corruption, you will need to add a stringified JSON object as a query parameter to the proxied URL. Each corruption has a unique configuration JSON object template. Each object can be used to target one specific segment for corruption. -e.i. `https:///api/v2/manifests/hls/proxy-master.m3u8?url=?some_corruption=[{i:0},{i:1},{i:2}, ... ,{i:N}]` +e.i. `https:///api/v2/manifests/hls/proxy-master.m3u8?url=&some_corruption=[{i:0},{i:1},{i:2}, ... ,{i:N}]` Across all corruptions, there are 3 ways to target a segment in a playlist for corruption. diff --git a/src/manifests/utils/configs.ts b/src/manifests/utils/configs.ts index 38bfc47..bf678bb 100644 --- a/src/manifests/utils/configs.ts +++ b/src/manifests/utils/configs.ts @@ -182,7 +182,6 @@ export const corruptorConfigUtils = function ( rsq < 0 && playlistSize > 0 ? mseqOffset + playlistSize + rsq + 1 : Number(param.rsq) + mseqOffset; - console.log('sq: ' + param.sq + ' | rsq: ' + Number(param.rsq)); delete param.rsq; } return param; From f4497e0f3c50df1df52151e8ea3f184786065e27 Mon Sep 17 00:00:00 2001 From: Robin Olsson Date: Thu, 1 Feb 2024 16:35:14 +0100 Subject: [PATCH 3/4] chore: added info about the use of negative rsq values to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 09b5aab..9797245 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Across all corruptions, there are 3 ways to target a segment in a playlist for c 1. `i`: The segment's list index in any Media Playlist, with HLS segments starting at 0 and MPEG-DASH segments starting at 1. For a Media Playlist with 12 segments, `i`=11, would target the last segment for HLS and `i`=12, would target the last segment for MPEG-DASH. 2. `sq`: The segment's Media Sequence Number for HLS, or the "$Number$" or "$Time$" part of a segment URL for DASH. For an HLS Media Playlist with 12 segments, and where `#EXT-X-MEDIA-SEQUENCE` is 100, `sq`=111 would target the last segment. When corrupting a live HLS stream it is recommended to target with `rsq`. -3. `rsq`: A relative sequence number, counted from where the live stream is currently at when requesting manifest. (**HLS SUPPORTED ONLY IN STATEFUL MODE**) +3. `rsq`: A relative sequence number, counted from where the live stream is currently at when requesting manifest. Can also use a negative integer, which enables counting backwards from the end of the manifest instead. (**HLS SUPPORTED ONLY IN STATEFUL MODE**) Below are configuration JSON object templates for the currently supported corruptions. A query should have its value be an array consisting of any one of these 3 types of items: From 136f52f5e4c390710bcc07d487fcafaed8c389cc Mon Sep 17 00:00:00 2001 From: Robin Olsson Date: Thu, 1 Feb 2024 16:46:25 +0100 Subject: [PATCH 4/4] fix: removed console.log left over from testing --- src/manifests/utils/configs.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/manifests/utils/configs.test.ts b/src/manifests/utils/configs.test.ts index 88b5841..d145658 100644 --- a/src/manifests/utils/configs.test.ts +++ b/src/manifests/utils/configs.test.ts @@ -232,7 +232,6 @@ describe('configs', () => { const [err, actual] = configs.getAllManifestConfigs(0, false, 100, 15); // Assert - console.log(actual); expect(err).toBeNull(); expect(actual.get(115)).toEqual( new Map([