Files
shaka-player/lib/net/http_xhr_plugin.js
T
Michelle Zhuo 40f9113bb2 Abort requests when network downgrading
When the network becomes slow, we check if stopping the current request
and download the content with lower resolution is faster. If so, abort
the current request and start a new one.

Issue #1051

Change-Id: I588e524469432e362361d1cfbde6cd45c2009959
2019-05-01 20:18:16 +00:00

143 lines
4.8 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');
/**
* @namespace
* @summary A networking plugin to handle http and https URIs via XHR.
* @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
*/
shaka.net.HttpXHRPlugin = function(uri, request, requestType, progressUpdated) {
let 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;
let promise = new Promise(function(resolve, reject) {
xhr.open(request.method, uri, true);
xhr.responseType = 'arraybuffer';
xhr.timeout = request.retryParameters.timeout;
xhr.withCredentials = request.allowCrossSiteCredentials;
xhr.onabort = function() {
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 = function(event) {
let 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 {
let 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 = function(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 = function(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 = function(event) {
let 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 (let key in request.headers) {
// The Fetch API automatically normalizes outgoing header keys to
// lowercase. For consistency's sake, do it here too.
let 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,
shaka.net.NetworkingEngine.PluginPriority.FALLBACK);
shaka.net.NetworkingEngine.registerScheme('https', shaka.net.HttpXHRPlugin,
shaka.net.NetworkingEngine.PluginPriority.FALLBACK);