Skip to content

Commit

Permalink
fix: getLicense should pass an error to callback if XHR returns 400/5…
Browse files Browse the repository at this point in the history
…00 (#99)

* pass error to license xhr request callbacks for 400 and 500 response codes, include original error object in player error triggered in emeErrorHandler

* update readme

* change letter case

* dont pass original error, use default eme error message for 400/500s, update test

* change readme section title, add unit tests
  • Loading branch information
alex-barstow committed Feb 6, 2020
1 parent c574b43 commit 498ebaf
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 4 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Maintenance Status: Stable
- [Other DRM Systems](#other-drm-systems)
- [Get License By URL](#get-license-by-url)
- [Get License By Function](#get-license-by-function)
- [Get License Errors](#get-license-errors)
- [API](#api)
- [Available Options](#available-options)
- [`keySystems`](#keysystems)
Expand Down Expand Up @@ -278,6 +279,10 @@ Below is an example of using one of these DRM systems and custom `getLicense()`
}
```

### Get License Errors

The default `getLicense()` functions pass an error to the callback if the license request returns a 4xx or 5xx response code. Depending on how the license server is configured, it is possible in some cases that a valid license could still be returned even if the response code is in that range. If you wish not to pass an error for 4xx and 5xx response codes, you may pass your own `getLicense()` function with the `keySystems` as described above.

## API

### Available Options
Expand Down Expand Up @@ -490,7 +495,7 @@ When `suppressErrorsIfPossible` is set to `false` (the default) and an error occ

### Events

There are some events that are specific to this plugin.
There are some events that are specific to this plugin.

#### `licenserequestattempted`

Expand Down
8 changes: 7 additions & 1 deletion src/eme.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ const defaultPlayreadyGetLicense = (keySystemOptions) => (emeOptions, keyMessage
requestPlayreadyLicense(keySystemOptions, keyMessage, emeOptions, callback);
};

const defaultGetLicense = (keySystemOptions) => (emeOptions, keyMessage, callback) => {
export const defaultGetLicense = (keySystemOptions) => (emeOptions, keyMessage, callback) => {
const headers = mergeAndRemoveNull(
{'Content-type': 'application/octet-stream'},
emeOptions.emeHeaders,
Expand All @@ -195,6 +195,12 @@ const defaultGetLicense = (keySystemOptions) => (emeOptions, keyMessage, callbac
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);
});
};
Expand Down
6 changes: 6 additions & 0 deletions src/fairplay.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ export const defaultGetLicense = (fairplayOptions) => {
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);
});
};
Expand Down
6 changes: 6 additions & 0 deletions src/playready.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ export const requestPlayreadyLicense = (keySystemOptions, messageBuffer, emeOpti
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);
});
};
4 changes: 2 additions & 2 deletions src/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export const setupSessions = (player) => {
*/
export const emeErrorHandler = (player) => {
return (objOrErr) => {
const message = objOrErr ? objOrErr.message || objOrErr : null;
const message = typeof objOrErr === 'string' ? objOrErr : (objOrErr && objOrErr.message) || null;

player.error({
// MEDIA_ERR_ENCRYPTED is code 5
Expand Down Expand Up @@ -294,7 +294,7 @@ const eme = function(options = {}) {
* @param {Object} [emeOptions={}]
* An object of eme plugin options.
* @param {Function} [callback=function(){}]
* @param {Boolean} [suppressErrorIfPossible=false]
* @param {boolean} [suppressErrorIfPossible=false]
*/
initializeMediaKeys(emeOptions = {}, callback = function() {}, suppressErrorIfPossible = false) {
// TODO: this should be refactored and renamed to be less tied
Expand Down
54 changes: 54 additions & 0 deletions test/eme.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import QUnit from 'qunit';
import videojs from 'video.js';
import {
defaultGetLicense,
standard5July2016,
makeNewRequest,
getSupportedKeySystem
} from '../src/eme';
import sinon from 'sinon';

// mock session to make testing easier (so we can trigger events)
const getMockSession = () => {
Expand Down Expand Up @@ -717,6 +719,58 @@ QUnit.test('getLicense promise rejection', function(assert) {

});

QUnit.test('getLicense calls back with error for 400 and 500 status codes', function(assert) {
const getLicenseCallback = sinon.spy();
const getLicense = defaultGetLicense({});

videojs.xhr = (params, callback) => {
return callback(null, {statusCode: 400}, {body: 'some-body'});
};

getLicense({}, null, getLicenseCallback);

videojs.xhr = (params, callback) => {
return callback(null, {statusCode: 500}, {body: 'some-body'});
};

getLicense({}, null, getLicenseCallback);

videojs.xhr = (params, callback) => {
return callback(null, {statusCode: 599}, {body: 'some-body'});
};

getLicense({}, null, getLicenseCallback);

assert.equal(getLicenseCallback.callCount, 3, 'correct callcount');
assert.equal(getLicenseCallback.alwaysCalledWith({}), true, 'getLicense callback called with correct error');
});

QUnit.test('getLicense calls back with response body for non-400/500 status codes', function(assert) {
const getLicenseCallback = sinon.spy();
const getLicense = defaultGetLicense({});

videojs.xhr = (params, callback) => {
return callback(null, {statusCode: 200}, {body: 'some-body'});
};

getLicense({}, null, getLicenseCallback);

videojs.xhr = (params, callback) => {
return callback(null, {statusCode: 399}, {body: 'some-body'});
};

getLicense({}, null, getLicenseCallback);

videojs.xhr = (params, callback) => {
return callback(null, {statusCode: 600}, {body: 'some-body'});
};

getLicense({}, null, getLicenseCallback);

assert.equal(getLicenseCallback.callCount, 3, 'correct callcount');
assert.equal(getLicenseCallback.alwaysCalledWith(null, {body: 'some-body'}), true, 'getLicense callback called with correct args');
});

QUnit.test('keySession.update promise rejection', function(assert) {
const options = {
keySystems: {
Expand Down
3 changes: 3 additions & 0 deletions test/plugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,9 @@ QUnit.test('emeError properly handles various parameter types', function(assert)
emeError(undefined);
assert.equal(errorObj.message, null, 'null error message');

emeError({});
assert.equal(errorObj.message, null, 'null error message');

emeError(new Error('some error'));
assert.equal(errorObj.message, 'some error', 'use error text when error');

Expand Down

0 comments on commit 498ebaf

Please sign in to comment.