Browse Source

Use url-toolkit for url manipulation (#913)

pull/6/head
forbesjo 9 years ago
committed by GitHub
parent
commit
d7575679c2
  1. 6
      LICENSE
  2. 7
      package.json
  3. 99
      src/resolve-url.js
  4. 47
      test/resolve-url.test.js

6
LICENSE

@ -12,12 +12,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
src/resolve-url.js implementation in this project is derived from
the HLS library hls.js (https://github.com/dailymotion/hls.js)
That work is also covered by the Apache 2 License, following copyright:
Copyright (c) 2015 Dailymotion (http://www.dailymotion.com)
The AES decryption implementation in this project is derived from the
Stanford Javascript Cryptography Library
(http://bitwiseshiftleft.github.io/sjcl/). That work is covered by the

7
package.json

@ -89,13 +89,14 @@
"test/"
],
"dependencies": {
"m3u8-parser": "^1.0.2",
"aes-decrypter": "^1.0.3",
"global": "^4.3.0",
"m3u8-parser": "^1.0.2",
"mux.js": "^3.0.0",
"url-toolkit": "^1.0.4",
"video.js": "^5.10.1",
"videojs-contrib-media-sources": "^4.1.0",
"videojs-swf": "^5.0.2",
"global": "^4.3.0"
"videojs-swf": "^5.0.2"
},
"devDependencies": {
"babel": "^5.8.0",

99
src/resolve-url.js

@ -2,112 +2,21 @@
* @file resolve-url.js
*/
import URLToolkit from 'url-toolkit';
import window from 'global/window';
// From hls.js https://github.com/dailymotion/hls.js
// build an absolute path using the provided basePath
// adapted from https://developer.mozilla.org/en-US/docs/Web/API/document/cookie#Using_relative_URLs_in_the_path_parameter
// this does not handle the case where relativePath is "/" or "//". These cases should be handled outside this.
const buildAbsolutePath = function(basePath, relativePath) {
let sRelPath = relativePath;
let nUpLn;
let sDir = '';
let sPath = basePath.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, '$1'));
let nEnd;
let nStart;
for (nEnd, nStart = 0; (nEnd = sPath.indexOf('/../', nStart), nEnd > -1); nStart = nEnd + nUpLn) {
nUpLn = (/^\/(?:\.\.\/)*/).exec(sPath.slice(nEnd))[0].length;
sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp('(?:\\\/+[^\\\/]*){0,' + ((nUpLn - 1) / 3) + '}$'), '/');
}
return sDir + sPath.substr(nStart);
};
// build an absolute URL from a relative one using the provided baseURL
// if relativeURL is an absolute URL it will be returned as is.
const buildAbsoluteURL = function(baseURL, relativeURL) {
let relativeURLQuery = null;
let relativeURLHash = null;
let relativeURLHashSplit = (/^([^#]*)(.*)$/).exec(relativeURL);
if (relativeURLHashSplit) {
relativeURLHash = relativeURLHashSplit[2];
relativeURL = relativeURLHashSplit[1];
}
let relativeURLQuerySplit = (/^([^\?]*)(.*)$/).exec(relativeURL);
if (relativeURLQuerySplit) {
relativeURLQuery = relativeURLQuerySplit[2];
relativeURL = relativeURLQuerySplit[1];
}
let baseURLHashSplit = (/^([^#]*)(.*)$/).exec(baseURL);
if (baseURLHashSplit) {
baseURL = baseURLHashSplit[1];
}
let baseURLQuerySplit = (/^([^\?]*)(.*)$/).exec(baseURL);
if (baseURLQuerySplit) {
baseURL = baseURLQuerySplit[1];
}
let baseURLDomainSplit = (/^(([a-z]+:)?\/\/[a-z0-9\.\-_~]+(:[0-9]+)?)?(\/.*)$/i).exec(baseURL);
if (!baseURLDomainSplit) {
throw new Error('Error trying to parse base URL.');
}
// e.g. 'http:', 'https:', ''
let baseURLProtocol = baseURLDomainSplit[2] || '';
// e.g. 'http://example.com', '//example.com', ''
let baseURLProtocolDomain = baseURLDomainSplit[1] || '';
// e.g. '/a/b/c/playlist.m3u8'
let baseURLPath = baseURLDomainSplit[4];
let builtURL = null;
if ((/^\/\//).test(relativeURL)) {
// relative url starts wth '//' so copy protocol (which may be '' if baseUrl didn't provide one)
builtURL = baseURLProtocol + '//' + buildAbsolutePath('', relativeURL.substring(2));
} else if ((/^\//).test(relativeURL)) {
// relative url starts with '/' so start from root of domain
builtURL = baseURLProtocolDomain + '/' + buildAbsolutePath('', relativeURL.substring(1));
} else {
builtURL = buildAbsolutePath(baseURLProtocolDomain + baseURLPath, relativeURL);
}
// put the query and hash parts back
if (relativeURLQuery) {
builtURL += relativeURLQuery;
}
if (relativeURLHash) {
builtURL += relativeURLHash;
}
return builtURL;
};
const resolveUrl = function(baseURL, relativeURL) {
// remove any remaining space and CRLF
relativeURL = relativeURL.trim();
// return early if we don't need to resolve
if ((/^[a-z]+:/i).test(relativeURL)) {
// complete url, not relative
return relativeURL;
}
// if the base URL is relative then combine with the current location
if (!(/\/\//i).test(baseURL)) {
baseURL = buildAbsoluteURL(window.location.href, baseURL);
baseURL = URLToolkit.buildAbsoluteURL(window.location.href, baseURL);
}
return buildAbsoluteURL(baseURL, relativeURL);
return URLToolkit.buildAbsoluteURL(baseURL, relativeURL);
};
export default resolveUrl;

47
test/resolve-url.test.js

@ -2,7 +2,7 @@ import QUnit from 'qunit';
import window from 'global/window';
import resolveUrl from '../src/resolve-url';
// Tests pulled over from hls.js
// A modified subset of tests from https://github.com/tjenkinson/url-toolkit
QUnit.module('URL resolver');
@ -10,53 +10,8 @@ QUnit.test('works with a selection of valid urls', function() {
let currentLocation = window.location.protocol + '//' + window.location.host;
QUnit.equal(resolveUrl('http://a.com/b/cd/e.m3u8', 'https://example.com/z.ts'), 'https://example.com/z.ts');
QUnit.equal(resolveUrl('http://a.com/b/cd/e.m3u8', 'g:h'), 'g:h');
QUnit.equal(resolveUrl('http://a.com/b/cd/e.m3u8', 'https://example.com:8080/z.ts'), 'https://example.com:8080/z.ts');
QUnit.equal(resolveUrl('http://a.com/b/cd/e.m3u8', 'z.ts'), 'http://a.com/b/cd/z.ts');
QUnit.equal(resolveUrl('http://a.com:8080/b/cd/e.m3u8', 'z.ts'), 'http://a.com:8080/b/cd/z.ts');
QUnit.equal(resolveUrl('http://a.com/b/cd/', 'z.ts'), 'http://a.com/b/cd/z.ts');
QUnit.equal(resolveUrl('http://a.com/b/cd', 'z.ts'), 'http://a.com/b/z.ts');
QUnit.equal(resolveUrl('http://a.com/b/cd?test=1', 'z.ts'), 'http://a.com/b/z.ts');
QUnit.equal(resolveUrl('http://a.com/b/cd#something', 'z.ts'), 'http://a.com/b/z.ts');
QUnit.equal(resolveUrl('http://a.com/b/cd?test=1#something', 'z.ts'), 'http://a.com/b/z.ts');
QUnit.equal(resolveUrl('http://a.com/b/cd?test=1#something', 'z.ts?abc=1'), 'http://a.com/b/z.ts?abc=1');
QUnit.equal(resolveUrl('http://a.com/b/cd?test=1#something', 'z.ts#test'), 'http://a.com/b/z.ts#test');
QUnit.equal(resolveUrl('http://a.com/b/cd?test=1#something', 'z.ts?abc=1#test'), 'http://a.com/b/z.ts?abc=1#test');
QUnit.equal(resolveUrl('http://a.com/b/cd?test=1#something', ';x'), 'http://a.com/b/;x');
QUnit.equal(resolveUrl('http://a.com/b/cd?test=1#something', 'g;x'), 'http://a.com/b/g;x');
QUnit.equal(resolveUrl('http://a_b.com/b/cd?test=1#something', 'g;x'), 'http://a_b.com/b/g;x');
QUnit.equal(resolveUrl('http://a-b.com/b/cd?test=1#something', 'g;x'), 'http://a-b.com/b/g;x');
QUnit.equal(resolveUrl('http://a.b.com/b/cd?test=1#something', 'g;x'), 'http://a.b.com/b/g;x');
QUnit.equal(resolveUrl('http://a~b.com/b/cd?test=1#something', 'g;x'), 'http://a~b.com/b/g;x');
QUnit.equal(resolveUrl('http://a.com/b/cd/e.m3u8?test=1#something', 'subdir/z.ts?abc=1#test'), 'http://a.com/b/cd/subdir/z.ts?abc=1#test');
QUnit.equal(resolveUrl('http://a.com/b/cd/e.m3u8?test=1#something', '/subdir/z.ts?abc=1#test'), 'http://a.com/subdir/z.ts?abc=1#test');
QUnit.equal(resolveUrl('http://a.com/b/cd/e.m3u8?test=1#something', '//example.com/z.ts?abc=1#test'), 'http://example.com/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e.m3u8?test=1#something', '//example.com/z.ts?abc=1#test'), 'https://example.com/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e.m3u8?test=1#something', './z.ts?abc=1#test'), 'https://a.com/b/cd/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e.m3u8?test=1#something', '../z.ts?abc=1#test'), 'https://a.com/b/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e.m3u8?test=1#something', './../z.ts?abc=1#test'), 'https://a.com/b/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e.m3u8?test=1#something', '././z.ts?abc=1#test'), 'https://a.com/b/cd/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e/f.m3u8?test=1#something', '../../z.ts?abc=1#test'), 'https://a.com/b/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e.m3u8?test=1#something', '../../z.ts?abc=1#test'), 'https://a.com/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e.m3u8?test=1#something', '../../z.ts?abc=1&something=blah/./../test#test'), 'https://a.com/z.ts?abc=1&something=blah/./../test#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e/f.m3u8?test=1#something', './../../z.ts?abc=1#test'), 'https://a.com/b/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e.m3u8?test=1#something', 'subdir/pointless/../z.ts?abc=1#test'), 'https://a.com/b/cd/subdir/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e.m3u8?test=1#something', '/subdir/pointless/../z.ts?abc=1#test'), 'https://a.com/subdir/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a.com/b/cd/e.m3u8?test=1#something', '//example.com/subdir/pointless/../z.ts?abc=1#test'), 'https://example.com/subdir/z.ts?abc=1#test');
QUnit.equal(resolveUrl('https://a-b.something.com/b/cd/e.m3u8?test=1#something', '//example.com/subdir/pointless/../z.ts?abc=1#test'), 'https://example.com/subdir/z.ts?abc=1#test');
QUnit.equal(resolveUrl('//a.com/b/cd/e.m3u8', 'https://example.com/z.ts'), 'https://example.com/z.ts');
QUnit.equal(resolveUrl('//a.com/b/cd/e.m3u8', 'g:h'), 'g:h');
QUnit.equal(resolveUrl('//a.com/b/cd/e.m3u8', 'https://example.com:8080/z.ts'), 'https://example.com:8080/z.ts');
QUnit.equal(resolveUrl('//a.com/b/cd/e.m3u8', 'z.ts'), '//a.com/b/cd/z.ts');
QUnit.equal(resolveUrl('//a.com/b/cd/e.m3u8', '../../z.ts'), '//a.com/z.ts');
QUnit.equal(resolveUrl('/a/b/cd/e.m3u8', 'https://example.com/z.ts'), 'https://example.com/z.ts');
QUnit.equal(resolveUrl('/a/b/cd/e.m3u8', 'g:h'), 'g:h');
QUnit.equal(resolveUrl('/a/b/cd/e.m3u8', 'https://example.com:8080/z.ts'), 'https://example.com:8080/z.ts');
QUnit.equal(resolveUrl('/a/b/cd/e.m3u8', 'z.ts'), currentLocation + '/a/b/cd/z.ts');
QUnit.equal(resolveUrl('/a/b/cd/e.m3u8', '../../../z.ts'), currentLocation + '/z.ts');

Loading…
Cancel
Save