From f27401cc151a435ae8fb12be4e86d672c331e1e5 Mon Sep 17 00:00:00 2001 From: Casey Occhialini <1508707+littlespex@users.noreply.github.com> Date: Tue, 25 Jan 2022 14:42:20 -0800 Subject: [PATCH] fix: Fix duplicate CMCD parameters in HLS live content (#3875) Use goog.Uri to append CMCD query data to avoid duplicate query params Fixes #3862 Co-authored-by: Dan Sparacio --- lib/util/cmcd_manager.js | 8 +++++--- test/util/cmcd_manager_unit.js | 14 ++++++-------- third_party/closure-uri/uri.js | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/lib/util/cmcd_manager.js b/lib/util/cmcd_manager.js index 4caa230465..32d092437b 100644 --- a/lib/util/cmcd_manager.js +++ b/lib/util/cmcd_manager.js @@ -6,6 +6,7 @@ goog.provide('shaka.util.CmcdManager'); +goog.require('goog.Uri'); goog.require('shaka.log'); @@ -534,7 +535,7 @@ shaka.util.CmcdManager = class { * @return {string} */ static toQuery(data) { - return `CMCD=${encodeURIComponent(shaka.util.CmcdManager.serialize(data))}`; + return shaka.util.CmcdManager.serialize(data); } /** @@ -553,8 +554,9 @@ shaka.util.CmcdManager = class { return uri; } - const separator = uri.includes('?') ? '&' : '?'; - return `${uri}${separator}${query}`; + const url = new goog.Uri(uri); + url.getQueryData().set('CMCD', query); + return url.toString(); } }; diff --git a/test/util/cmcd_manager_unit.js b/test/util/cmcd_manager_unit.js index 196c5901a0..76799531af 100644 --- a/test/util/cmcd_manager_unit.js +++ b/test/util/cmcd_manager_unit.js @@ -33,13 +33,11 @@ describe('CmcdManager', () => { describe('Query serialization', () => { it('produces correctly serialized data', () => { const query = CmcdManager.toQuery(data); - const result = 'CMCD=br%3D52317%2Cbs%2Ccid%3D%22xyz%22%2C' + - 'com.test-exists%2Ccom.test-hello%3D%22world%22%2C' + - 'com.test-testing%3D1234%2Ccom.test-token%3Ds%2C' + - 'd%3D6067%2Cmtp%3D10000%2C' + - 'nor%3D%22..%252Ftesting%252F3.m4v%22%2C' + - 'nrr%3D%220-99%22%2C' + - 'sid%3D%22c936730c-031e-4a73-976f-92bc34039c60%22'; + const result = 'br=52317,bs,cid="xyz",com.test-exists,' + + 'com.test-hello="world",com.test-testing=1234,' + + 'com.test-token=s,d=6067,mtp=10000,' + + 'nor="..%2Ftesting%2F3.m4v",nrr="0-99",' + + 'sid="c936730c-031e-4a73-976f-92bc34039c60"'; expect(query).toBe(result); }); @@ -47,7 +45,7 @@ describe('CmcdManager', () => { const query = CmcdManager.toQuery({ 'com.test-escape': 'Double "Quotes"', }); - const result = 'CMCD=com.test-escape%3D%22Double%20%5C%22Quotes%5C%22%22'; + const result = 'com.test-escape="Double \\"Quotes\\""'; expect(query).toBe(result); }); }); diff --git a/third_party/closure-uri/uri.js b/third_party/closure-uri/uri.js index 2762bb3eaa..c0ab40beaa 100644 --- a/third_party/closure-uri/uri.js +++ b/third_party/closure-uri/uri.js @@ -808,6 +808,27 @@ goog.Uri.QueryData.prototype.add = function(key, value) { return this; }; +/** + * Sets a key value pair and removes all other keys with the same value. + * + * @param {string} key Name. + * @param {string} value Value. + * @return {!goog.Uri.QueryData} Instance of this object. + */ + goog.Uri.QueryData.prototype.set = function(key, value) { + this.ensureKeyMapInitialized_(); + // Invalidate the cache. + this.encodedQuery_ = null; + + if (!this.keyMap_.hasOwnProperty(key)) { + this.add(key, value); + } else { + this.keyMap_[key] = [value]; + } + + return this; +}; + /** * @return {string} Encoded query string.