Files
shaka-player/lib/net/http_xhr_plugin.js
T
Joey Parrish 393b0ecea7 Fix HTTP test failures
This makes progress callbacks into a required parameter on all network
scheme plugins.  This does not mean that every plugin must make use of
the callback, but every caller must supply the callback.

In production, NetworkingEngine already supplies this callback
universally, so our HTTP plugins make use of it whenever progress data
is available.

Our tests, however, did not always supply this callback, leading to
test failures.  These failures were more likely to show up in Jasmine
3 than Jasmine 2 for some reason, which caused us to downgrade back to
Jasmine 2 recently.

By making the callback required, we can clean up this inconsistency
between test and production and give the HTTP plugins what they expect
in all cases.

Issue #1949

Change-Id: I8a6e1904e73cf7ca6ae8f3964261c339f404854d
2019-05-22 15:42:06 +00:00

149 lines
5.0 KiB
JavaScript

/**
* @license
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.
*/
goog.provide('shaka.net.HttpXHRPlugin');
goog.require('goog.asserts');
goog.require('shaka.net.HttpPluginUtils');
goog.require('shaka.net.NetworkingEngine');
goog.require('shaka.util.AbortableOperation');
goog.require('shaka.util.Error');
/**
* @summary A networking plugin to handle http and https URIs via XHR.
* @export
*/
shaka.net.HttpXHRPlugin = class {
/**
* @param {string} uri
* @param {shaka.extern.Request} request
* @param {shaka.net.NetworkingEngine.RequestType} requestType
* @param {shaka.extern.ProgressUpdated} progressUpdated Called when a
* progress event happened.
* @return {!shaka.extern.IAbortableOperation.<shaka.extern.Response>}
* @export
*/
static parse(uri, request, requestType, progressUpdated) {
const xhr = new shaka.net.HttpXHRPlugin.Xhr_();
// Last time stamp when we got a progress event.
let lastTime = Date.now();
// Last number of bytes loaded, from progress event.
let lastLoaded = 0;
const promise = new Promise(((resolve, reject) => {
xhr.open(request.method, uri, true);
xhr.responseType = 'arraybuffer';
xhr.timeout = request.retryParameters.timeout;
xhr.withCredentials = request.allowCrossSiteCredentials;
xhr.onabort = () => {
reject(new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.NETWORK,
shaka.util.Error.Code.OPERATION_ABORTED,
uri, requestType));
};
xhr.onload = (event) => {
const target = event.target;
goog.asserts.assert(target, 'XHR onload has no target!');
// Since IE and Edge incorrectly return the header with a leading new
// line character ('\n'), we trim the header here.
const headerLines = target.getAllResponseHeaders().trim().split('\r\n');
const headers = {};
for (const header of headerLines) {
/** @type {!Array.<string>} */
const parts = header.split(': ');
headers[parts[0].toLowerCase()] = parts.slice(1).join(': ');
}
try {
const response = shaka.net.HttpPluginUtils.makeResponse(headers,
target.response, target.status, uri, target.responseURL,
requestType);
resolve(response);
} catch (error) {
goog.asserts.assert(error instanceof shaka.util.Error,
'Wrong error type!');
reject(error);
}
};
xhr.onerror = (event) => {
reject(new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.NETWORK,
shaka.util.Error.Code.HTTP_ERROR,
uri, event, requestType));
};
xhr.ontimeout = (event) => {
reject(new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.NETWORK,
shaka.util.Error.Code.TIMEOUT,
uri, requestType));
};
xhr.onprogress = (event) => {
const currentTime = Date.now();
// If the time between last time and this time we got progress event
// is long enough, or if a whole segment is downloaded, call
// progressUpdated().
if (currentTime - lastTime > 100 ||
(event.lengthComputable && event.loaded == event.total)) {
progressUpdated(currentTime - lastTime, event.loaded - lastLoaded,
event.total - event.loaded);
lastLoaded = event.loaded;
lastTime = currentTime;
}
};
for (const key in request.headers) {
// The Fetch API automatically normalizes outgoing header keys to
// lowercase. For consistency's sake, do it here too.
const lowercasedKey = key.toLowerCase();
xhr.setRequestHeader(lowercasedKey, request.headers[key]);
}
xhr.send(request.body);
}));
return new shaka.util.AbortableOperation(
promise,
() => {
xhr.abort();
return Promise.resolve();
});
}
};
/**
* Overridden in unit tests, but compiled out in production.
*
* @const {function(new: XMLHttpRequest)}
* @private
*/
shaka.net.HttpXHRPlugin.Xhr_ = window.XMLHttpRequest;
shaka.net.NetworkingEngine.registerScheme(
'http', shaka.net.HttpXHRPlugin.parse,
shaka.net.NetworkingEngine.PluginPriority.FALLBACK);
shaka.net.NetworkingEngine.registerScheme(
'https', shaka.net.HttpXHRPlugin.parse,
shaka.net.NetworkingEngine.PluginPriority.FALLBACK);