From 836bd9a17727fec7b563c21a62c601450421ef13 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 26 Jan 2017 11:04:07 -0800 Subject: [PATCH] Make filters, requests, and responses easier By exporting StringUtils and Uint8ArrayUtils, we can simplify the process of wrapping, unwrapping, and parsing requests and responses in NetworkingEngine filters. This makes it easier to write filters that deal with text-based data, as it is no longer the job of the application developer to deal with subtle conversions between string and ArrayBuffer. We will also fill in requests with defaults if needed. This allows developers to omit fields when initiating extra requests at the application level. Closes #667 Change-Id: Ie6a0f23b4d46e7e458d996759eac6cd85a36b741 --- docs/tutorials/license-wrapping.md | 37 +++++++++++++----------------- lib/net/networking_engine.js | 14 +++++++---- lib/util/string_utils.js | 5 ++++ lib/util/uint8array_utils.js | 6 +++++ 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/docs/tutorials/license-wrapping.md b/docs/tutorials/license-wrapping.md index 2b6db05bc..2dd419bb0 100644 --- a/docs/tutorials/license-wrapping.md +++ b/docs/tutorials/license-wrapping.md @@ -60,11 +60,12 @@ license request, we must register a request filter: ```js player.getNetworkingEngine().registerRequestFilter(function(type, request) { + // Alias some utilities provided by the library. + var StringUtils = shaka.util.StringUtils; + var Uint8ArrayUtils = shaka.util.Uint8ArrayUtils; + // Only manipulate license requests: if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) { - // This is the raw license request generated by the Widevine CDM. - var rawLicenseRequest = new Uint8Array(request.body); - // Create the wrapped request structure. var wrapped = {}; @@ -72,7 +73,7 @@ license request, we must register a request filter: // The server we are using in this tutorial expects this field and this // encoding for the raw request. wrapped.rawLicenseRequestBase64 = - btoa(String.fromCharCode.apply(null, rawLicenseRequest)); + Uint8ArrayUtils.toBase64(new Uint8Array(request.body)); // Add whatever else we want to communicate to the server. // None of these values are read by the server we are using in this @@ -86,12 +87,9 @@ license request, we must register a request filter: // Encode the wrapped request as JSON. var wrappedJson = JSON.stringify(wrapped); - // Convert the JSON string back into a Uint8Array to replace the request + // Convert the JSON string back into an ArrayBuffer to replace the request // body. - request.body = new Uint8Array(wrappedJson.length); - for (var i = 0; i < wrappedJson.length; ++i) { - request.body[i] = wrappedJson.charCodeAt(i); - } + request.body = StringUtils.toUTF8(wrappedJson); } }); ``` @@ -133,25 +131,22 @@ using a request filter: ```js player.getNetworkingEngine().registerResponseFilter(function(type, response) { + // Alias some utilities provided by the library. + var StringUtils = shaka.util.StringUtils; + var Uint8ArrayUtils = shaka.util.Uint8ArrayUtils; + // Only manipulate license responses: if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) { - // This is the wrapped license. - var wrappedArray = new Uint8Array(response.data); - // Convert it to a string. - var wrappedString = String.fromCharCode.apply(null, wrappedArray); + // This is the wrapped license, which is a JSON string. + var wrappedString = StringUtils.fromUTF8(response.data); // Parse the JSON string into an object. var wrapped = JSON.parse(wrappedString); // This is a base64-encoded version of the raw license. var rawLicenseBase64 = wrapped.rawLicenseBase64; - // Decode it to a string. - var rawLicenseString = atob(rawLicenseBase64); - // Convert that string into a Uint8Array and replace the response data - // to feed it to the Widevine CDM. - response.data = new Uint8Array(rawLicenseString.length); - for (var i = 0; i < rawLicenseString.length; ++i) { - response.data[i] = rawLicenseString.charCodeAt(i); - } + // Decode that base64 string into a Uint8Array and replace the response + // data. The raw license will be fed to the Widevine CDM. + response.data = Uint8ArrayUtils.fromBase64(rawLicenseBase64); // Read additional fields from the server. // The server we are using in this tutorial does not send anything useful. diff --git a/lib/net/networking_engine.js b/lib/net/networking_engine.js index 1b3dd665f..741d054a7 100644 --- a/lib/net/networking_engine.js +++ b/lib/net/networking_engine.js @@ -252,12 +252,19 @@ shaka.net.NetworkingEngine.prototype.request = function(type, request) { goog.asserts.assert(request.uris && request.uris.length, 'Request without URIs!'); + // If a request comes from outside the library, some parameters may be left + // undefined. To make it easier for application developers, we will fill them + // in with defaults if necessary. + request.method = request.method || 'GET'; + request.headers = request.headers || {}; + request.retryParameters = request.retryParameters || + shaka.net.NetworkingEngine.defaultRetryParameters(); + var filterStartMs = Date.now(); // Send to the filter first, in-case they change the URI. - var requestFilters = this.requestFilters_; var p = Promise.resolve(); - requestFilters.forEach(function(requestFilter) { + this.requestFilters_.forEach(function(requestFilter) { // Request filters are resolved sequentially. p = p.then(function() { return Promise.resolve(requestFilter(type, request)); @@ -359,9 +366,8 @@ shaka.net.NetworkingEngine.prototype.send_ = function( response.timeMs = Date.now() - startTimeMs; var filterStartMs = Date.now(); - var responseFilters = this.responseFilters_; var p = Promise.resolve(); - responseFilters.forEach(function(responseFilter) { + this.responseFilters_.forEach(function(responseFilter) { // Response filters are resolved sequentially. p = p.then(function() { return Promise.resolve(responseFilter(type, response)); diff --git a/lib/util/string_utils.js b/lib/util/string_utils.js index 35c060415..f219ddb31 100644 --- a/lib/util/string_utils.js +++ b/lib/util/string_utils.js @@ -24,6 +24,7 @@ goog.require('shaka.util.Error'); /** * @namespace shaka.util.StringUtils * @summary A set of string utility functions. + * @exportDoc */ @@ -33,6 +34,7 @@ goog.require('shaka.util.Error'); * @param {?BufferSource} data * @return {string} * @throws {shaka.util.Error} + * @export */ shaka.util.StringUtils.fromUTF8 = function(data) { if (!data) return ''; @@ -69,6 +71,7 @@ shaka.util.StringUtils.fromUTF8 = function(data) { * @param {boolean} littleEndian true to read little endian, false to read big. * @return {string} * @throws {shaka.util.Error} + * @export */ shaka.util.StringUtils.fromUTF16 = function(data, littleEndian) { if (!data) return ''; @@ -110,6 +113,7 @@ shaka.util.StringUtils.fromUTF16 = function(data, littleEndian) { * @param {?BufferSource} data * @return {string} * @throws {shaka.util.Error} + * @export */ shaka.util.StringUtils.fromBytesAutoDetect = function(data) { var StringUtils = shaka.util.StringUtils; @@ -146,6 +150,7 @@ shaka.util.StringUtils.fromBytesAutoDetect = function(data) { * * @param {string} str * @return {!ArrayBuffer} + * @export */ shaka.util.StringUtils.toUTF8 = function(str) { // http://stackoverflow.com/a/13691499 diff --git a/lib/util/uint8array_utils.js b/lib/util/uint8array_utils.js index 38350840f..1e2aa353e 100644 --- a/lib/util/uint8array_utils.js +++ b/lib/util/uint8array_utils.js @@ -21,6 +21,7 @@ goog.provide('shaka.util.Uint8ArrayUtils'); /** * @namespace shaka.util.Uint8ArrayUtils * @summary A set of Uint8Array utility functions. + * @exportDoc */ @@ -31,6 +32,7 @@ goog.provide('shaka.util.Uint8ArrayUtils'); * @param {boolean=} opt_padding If true, pad the output with equals signs. * Defaults to true. * @return {string} + * @export */ shaka.util.Uint8ArrayUtils.toBase64 = function(arr, opt_padding) { // btoa expects a "raw string" where each character is interpreted as a byte. @@ -46,6 +48,7 @@ shaka.util.Uint8ArrayUtils.toBase64 = function(arr, opt_padding) { * alphabet or the alternate "base64url" alphabet. * @param {string} str * @return {!Uint8Array} + * @export */ shaka.util.Uint8ArrayUtils.fromBase64 = function(str) { // atob creates a "raw string" where each character is interpreted as a byte. @@ -62,6 +65,7 @@ shaka.util.Uint8ArrayUtils.fromBase64 = function(str) { * Convert a hex string to a Uint8Array. * @param {string} str * @return {!Uint8Array} + * @export */ shaka.util.Uint8ArrayUtils.fromHex = function(str) { var arr = new Uint8Array(str.length / 2); @@ -76,6 +80,7 @@ shaka.util.Uint8ArrayUtils.fromHex = function(str) { * Convert a Uint8Array to a hex string. * @param {!Uint8Array} arr * @return {string} + * @export */ shaka.util.Uint8ArrayUtils.toHex = function(arr) { var hex = ''; @@ -93,6 +98,7 @@ shaka.util.Uint8ArrayUtils.toHex = function(arr) { * @param {Uint8Array} arr1 * @param {Uint8Array} arr2 * @return {boolean} + * @export */ shaka.util.Uint8ArrayUtils.equal = function(arr1, arr2) { if (!arr1 && !arr2) return true;