diff --git a/src/eme.js b/src/eme.js index afd0f7b..8a8976b 100644 --- a/src/eme.js +++ b/src/eme.js @@ -2,6 +2,7 @@ import videojs from 'video.js'; import { requestPlayreadyLicense } from './playready'; import window from 'global/window'; import {mergeAndRemoveNull} from './utils'; +import {httpResponseHandler} from './http-handler.js'; /** * Returns an array of MediaKeySystemConfigurationObjects provided in the keySystem @@ -284,20 +285,8 @@ export const defaultGetLicense = (keySystemOptions) => (emeOptions, keyMessage, responseType: 'arraybuffer', body: keyMessage, headers - }, (err, response, responseBody) => { - if (err) { - callback(err); - return; - } - - if (response.statusCode >= 400 && response.statusCode <= 599) { - // Pass an empty object as the error to use the default code 5 error message - callback({}); - return; - } - - callback(null, responseBody); - }); + }, httpResponseHandler(callback, true) + ); }; const promisifyGetLicense = (getLicenseFn, eventBus) => { diff --git a/src/fairplay.js b/src/fairplay.js index 15eb104..3ae97b7 100644 --- a/src/fairplay.js +++ b/src/fairplay.js @@ -7,6 +7,7 @@ import videojs from 'video.js'; import window from 'global/window'; import {stringToUint16Array, uint8ArrayToString, getHostnameFromUri, mergeAndRemoveNull} from './utils'; +import {httpResponseHandler} from './http-handler.js'; export const FAIRPLAY_KEY_SYSTEM = 'com.apple.fps.1_0'; @@ -112,14 +113,17 @@ export const defaultGetCertificate = (fairplayOptions) => { uri: fairplayOptions.certificateUri, responseType: 'arraybuffer', headers - }, (err, response, responseBody) => { + }, httpResponseHandler((err, license) => { if (err) { callback(err); return; } - callback(null, new Uint8Array(responseBody)); - }); + // in this case, license is still the raw ArrayBuffer, + // (we don't want httpResponseHandler to decode it) + // convert it into Uint8Array as expected + callback(null, new Uint8Array(license)); + })); }; }; @@ -141,20 +145,7 @@ export const defaultGetLicense = (fairplayOptions) => { responseType: 'arraybuffer', body: keyMessage, headers - }, (err, response, responseBody) => { - if (err) { - callback(err); - return; - } - - if (response.statusCode >= 400 && response.statusCode <= 599) { - // Pass an empty object as the error to use the default code 5 error message - callback({}); - return; - } - - callback(null, responseBody); - }); + }, httpResponseHandler(callback, true)); }; }; diff --git a/src/http-handler.js b/src/http-handler.js new file mode 100644 index 0000000..155b606 --- /dev/null +++ b/src/http-handler.js @@ -0,0 +1,30 @@ +import videojs from 'video.js'; + +let httpResponseHandler = videojs.xhr.httpHandler; + +// to make sure this doesn't break with older versions of Video.js, +// do a super simple wrapper instead +if (!httpResponseHandler) { + httpResponseHandler = (callback, decodeResponseBody) => (err, response, responseBody) => { + if (err) { + callback(err); + return; + } + + // if the HTTP status code is 4xx or 5xx, the request also failed + if (response.statusCode >= 400 && response.statusCode <= 599) { + let cause = responseBody; + + if (decodeResponseBody) { + cause = String.fromCharCode.apply(null, new Uint8Array(responseBody)); + } + + callback({cause}); + return; + } + + // otherwise, request succeeded + callback(null, responseBody); + }; +} +export { httpResponseHandler }; diff --git a/src/playready.js b/src/playready.js index bc00e7a..8dbfccd 100644 --- a/src/playready.js +++ b/src/playready.js @@ -1,6 +1,7 @@ import videojs from 'video.js'; import window from 'global/window'; import {mergeAndRemoveNull} from './utils'; +import {httpResponseHandler} from './http-handler.js'; /** * Parses the EME key message XML to extract HTTP headers and the Challenge element to use @@ -57,18 +58,5 @@ export const requestPlayreadyLicense = (keySystemOptions, messageBuffer, emeOpti headers, body: message, responseType: 'arraybuffer' - }, (err, response, responseBody) => { - if (err) { - callback(err); - return; - } - - if (response.statusCode >= 400 && response.statusCode <= 599) { - // Pass an empty object as the error to use the default code 5 error message - callback({}); - return; - } - - callback(null, responseBody); - }); + }, httpResponseHandler(callback, true)); }; diff --git a/src/plugin.js b/src/plugin.js index 633bb15..da16959 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -185,13 +185,25 @@ export const setupSessions = (player) => { */ export const emeErrorHandler = (player) => { return (objOrErr) => { - const message = typeof objOrErr === 'string' ? objOrErr : (objOrErr && objOrErr.message) || null; - - player.error({ + const error = { // MEDIA_ERR_ENCRYPTED is code 5 - code: 5, - message - }); + code: 5 + }; + + if (typeof objOrErr === 'string') { + error.message = objOrErr; + } else if (objOrErr) { + if (objOrErr.message) { + error.message = objOrErr.message; + } + if (objOrErr.cause && + (objOrErr.cause.length || + objOrErr.cause.byteLength)) { + error.cause = objOrErr.cause; + } + } + + player.error(error); }; }; diff --git a/test/eme.test.js b/test/eme.test.js index 0fda473..23a2a8f 100644 --- a/test/eme.test.js +++ b/test/eme.test.js @@ -797,26 +797,39 @@ QUnit.test('getLicense calls back with error for 400 and 500 status codes', func const getLicenseCallback = sinon.spy(); const getLicense = defaultGetLicense({}); + function toArrayBuffer(obj) { + const json = JSON.stringify(obj); + const buffer = new ArrayBuffer(json.length); + const bufferView = new Uint8Array(buffer); + + for (let i = 0; i < json.length; i++) { + bufferView[i] = json.charCodeAt(i); + } + return buffer; + } + videojs.xhr = (params, callback) => { - return callback(null, {statusCode: 400}, {body: 'some-body'}); + return callback(null, {statusCode: 400}, toArrayBuffer({body: 'some-body'})); }; getLicense({}, null, getLicenseCallback); videojs.xhr = (params, callback) => { - return callback(null, {statusCode: 500}, {body: 'some-body'}); + return callback(null, {statusCode: 500}, toArrayBuffer({body: 'some-body'})); }; getLicense({}, null, getLicenseCallback); videojs.xhr = (params, callback) => { - return callback(null, {statusCode: 599}, {body: 'some-body'}); + return callback(null, {statusCode: 599}, toArrayBuffer({body: 'some-body'})); }; getLicense({}, null, getLicenseCallback); assert.equal(getLicenseCallback.callCount, 3, 'correct callcount'); - assert.equal(getLicenseCallback.alwaysCalledWith({}), true, 'getLicense callback called with correct error'); + assert.ok(getLicenseCallback.alwaysCalledWith({ + cause: JSON.stringify({body: 'some-body'}) + }), 'getLicense callback called with correct error'); }); QUnit.test('getLicense calls back with response body for non-400/500 status codes', function(assert) {