UNPKG

shaka-player

Version:
1,100 lines (979 loc) 33.6 kB
/*! @license * Shaka Player * Copyright 2016 Google LLC * SPDX-License-Identifier: Apache-2.0 */ goog.provide('shaka.net.NetworkingEngine'); goog.provide('shaka.net.NetworkingEngine.AdvancedRequestType'); goog.provide('shaka.net.NetworkingEngine.RequestType'); goog.provide('shaka.net.NetworkingEngine.PendingRequest'); goog.require('goog.Uri'); goog.require('goog.asserts'); goog.require('shaka.net.Backoff'); goog.require('shaka.util.AbortableOperation'); goog.require('shaka.util.BufferUtils'); goog.require('shaka.util.Error'); goog.require('shaka.util.FakeEvent'); goog.require('shaka.util.FakeEventTarget'); goog.require('shaka.util.IDestroyable'); goog.require('shaka.util.ObjectUtils'); goog.require('shaka.util.OperationManager'); goog.require('shaka.util.Timer'); /** * @event shaka.net.NetworkingEngine.RetryEvent * @description Fired when the networking engine receives a recoverable error * and retries. * @property {string} type * 'retry' * @property {?shaka.util.Error} error * The error that caused the retry. If it was a non-Shaka error, this is set * to null. * @exportDoc */ /** * NetworkingEngine wraps all networking operations. This accepts plugins that * handle the actual request. A plugin is registered using registerScheme. * Each scheme has at most one plugin to handle the request. * * @implements {shaka.util.IDestroyable} * @export */ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget { /** * @param {shaka.net.NetworkingEngine.onProgressUpdated=} onProgressUpdated * Called when * a progress event is triggered. Passed the duration, in milliseconds, * that the request took, the number of bytes transferred, and the boolean * of whether the switching is allowed. * @param {shaka.net.NetworkingEngine.OnHeadersReceived=} onHeadersReceived * Called when the headers are received for a download. * @param {shaka.net.NetworkingEngine.OnDownloadCompleted= * } onDownloadCompleted Called when a download completed successfully. * @param {shaka.net.NetworkingEngine.OnDownloadFailed=} onDownloadFailed * Called when a download fails, for any reason. * @param {shaka.net.NetworkingEngine.OnRequest=} onRequest * Called when a request is made * @param {shaka.net.NetworkingEngine.OnRetry=} onRetry * Called when a request retry is made * @param {shaka.net.NetworkingEngine.OnResponse=} onResponse * Called when receive the response */ constructor(onProgressUpdated, onHeadersReceived, onDownloadCompleted, onDownloadFailed, onRequest, onRetry, onResponse) { super(); /** @private {boolean} */ this.destroyed_ = false; /** @private {!shaka.util.OperationManager} */ this.operationManager_ = new shaka.util.OperationManager(); /** @private {!Set<shaka.extern.RequestFilter>} */ this.requestFilters_ = new Set(); /** @private {!Set<shaka.extern.ResponseFilter>} */ this.responseFilters_ = new Set(); /** @private {?shaka.net.NetworkingEngine.onProgressUpdated} */ this.onProgressUpdated_ = onProgressUpdated || null; /** @private {?shaka.net.NetworkingEngine.OnHeadersReceived} */ this.onHeadersReceived_ = onHeadersReceived || null; /** @private {?shaka.net.NetworkingEngine.OnDownloadCompleted} */ this.onDownloadCompleted_ = onDownloadCompleted || null; /** @private {?shaka.net.NetworkingEngine.OnDownloadFailed} */ this.onDownloadFailed_ = onDownloadFailed || null; /** @private {?shaka.net.NetworkingEngine.OnRequest} */ this.onRequest_ = onRequest || null; /** @private {?shaka.net.NetworkingEngine.OnRetry} */ this.onRetry_ = onRetry || null; /** @private {?shaka.net.NetworkingEngine.OnResponse} */ this.onResponse_ = onResponse || null; /** @private {boolean} */ this.forceHTTP_ = false; /** @private {boolean} */ this.forceHTTPS_ = false; /** @private {number} */ this.minBytesForProgressEvents_ = 16e3; /** @private {!Map<string, string>} */ this.hostCommonAccessTokenMap_ = new Map(); } /** * @param {boolean} forceHTTP * @export */ setForceHTTP(forceHTTP) { this.forceHTTP_ = forceHTTP; } /** * @param {boolean} forceHTTPS * @export */ setForceHTTPS(forceHTTPS) { this.forceHTTPS_ = forceHTTPS; } /** * @param {number} minBytes */ setMinBytesForProgressEvents(minBytes) { this.minBytesForProgressEvents_ = minBytes; } /** * Registers a scheme plugin. This plugin will handle all requests with the * given scheme. If a plugin with the same scheme already exists, it is * replaced, unless the existing plugin is of higher priority. * If no priority is provided, this defaults to the highest priority of * APPLICATION. * * @param {string} scheme * @param {shaka.extern.SchemePlugin} plugin * @param {number=} priority * @param {boolean=} progressSupport * @export */ static registerScheme(scheme, plugin, priority, progressSupport = false) { goog.asserts.assert( priority == undefined || priority > 0, 'explicit priority must be > 0'); priority = priority || shaka.net.NetworkingEngine.PluginPriority.APPLICATION; const existing = shaka.net.NetworkingEngine.schemes_.get(scheme); if (!existing || priority >= existing.priority) { shaka.net.NetworkingEngine.schemes_.set(scheme, { priority: priority, plugin: plugin, progressSupport: progressSupport, }); } } /** * Removes a scheme plugin. * * @param {string} scheme * @export */ static unregisterScheme(scheme) { shaka.net.NetworkingEngine.schemes_.delete(scheme); } /** * Copies all of the filters from this networking engine into another. * @param {!shaka.net.NetworkingEngine} other */ copyFiltersInto(other) { for (const filter of this.requestFilters_) { other.requestFilters_.add(filter); } for (const filter of this.responseFilters_) { other.responseFilters_.add(filter); } } /** * Registers a new request filter. All filters are applied in the order they * are registered. * * @param {shaka.extern.RequestFilter} filter * @export */ registerRequestFilter(filter) { this.requestFilters_.add(filter); } /** * Removes a request filter. * * @param {shaka.extern.RequestFilter} filter * @export */ unregisterRequestFilter(filter) { this.requestFilters_.delete(filter); } /** * Clears all request filters. * * @export */ clearAllRequestFilters() { this.requestFilters_.clear(); } /** * Registers a new response filter. All filters are applied in the order they * are registered. * * @param {shaka.extern.ResponseFilter} filter * @export */ registerResponseFilter(filter) { this.responseFilters_.add(filter); } /** * Removes a response filter. * * @param {shaka.extern.ResponseFilter} filter * @export */ unregisterResponseFilter(filter) { this.responseFilters_.delete(filter); } /** * Clears all response filters. * * @export */ clearAllResponseFilters() { this.responseFilters_.clear(); } /** * Clears Common Access Token map. * * @export */ clearCommonAccessTokenMap() { this.hostCommonAccessTokenMap_.clear(); } /** * Gets a copy of the default retry parameters. * * @return {shaka.extern.RetryParameters} * * NOTE: The implementation moved to shaka.net.Backoff to avoid a circular * dependency between the two classes. * * @export */ static defaultRetryParameters() { return shaka.net.Backoff.defaultRetryParameters(); } /** * Makes a simple network request for the given URIs. * * @param {!Array<string>} uris * @param {shaka.extern.RetryParameters} retryParams * @param {?function(BufferSource):!Promise=} streamDataCallback * @return {shaka.extern.Request} * @export */ static makeRequest(uris, retryParams, streamDataCallback = null) { return { uris: uris, method: 'GET', body: null, headers: {}, allowCrossSiteCredentials: false, retryParameters: retryParams, licenseRequestType: null, sessionId: null, drmInfo: null, initData: null, initDataType: null, streamDataCallback: streamDataCallback, }; } /** * @override * @export */ destroy() { this.destroyed_ = true; this.requestFilters_.clear(); this.responseFilters_.clear(); this.hostCommonAccessTokenMap_.clear(); // FakeEventTarget implements IReleasable super.release(); return this.operationManager_.destroy(); } /** * Makes a network request and returns the resulting data. * * @param {shaka.net.NetworkingEngine.RequestType} type * @param {shaka.extern.Request} request * @param {shaka.extern.RequestContext=} context * @return {!shaka.net.NetworkingEngine.PendingRequest} * @export */ request(type, request, context) { const ObjectUtils = shaka.util.ObjectUtils; const numBytesRemainingObj = new shaka.net.NetworkingEngine.NumBytesRemainingClass(); // Reject all requests made after destroy is called. if (this.destroyed_) { const p = Promise.reject(new shaka.util.Error( shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.PLAYER, shaka.util.Error.Code.OPERATION_ABORTED)); // Silence uncaught rejection errors, which may otherwise occur any place // we don't explicitly handle aborted operations. p.catch(() => {}); return new shaka.net.NetworkingEngine.PendingRequest( p, () => Promise.resolve(), numBytesRemainingObj); } 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. // // We clone retryParameters and uris so that if a filter modifies the // request, it doesn't contaminate future requests. request.method = request.method || 'GET'; request.headers = request.headers || {}; request.retryParameters = request.retryParameters ? ObjectUtils.cloneObject(request.retryParameters) : shaka.net.NetworkingEngine.defaultRetryParameters(); request.uris = ObjectUtils.cloneObject(request.uris); // Apply the registered filters to the request. const requestFilterOperation = this.filterRequest_(type, request, context); const requestOperation = requestFilterOperation.chain( () => this.makeRequestWithRetry_(type, request, context, numBytesRemainingObj)); const responseFilterOperation = requestOperation.chain( (responseAndGotProgress) => this.filterResponse_(type, responseAndGotProgress, context)); // Keep track of time spent in filters. const requestFilterStartTime = Date.now(); let requestFilterMs = 0; requestFilterOperation.promise.then(() => { requestFilterMs = Date.now() - requestFilterStartTime; }, () => {}); // Silence errors in this fork of the Promise chain. let responseFilterStartTime = 0; requestOperation.promise.then(() => { responseFilterStartTime = Date.now(); }, () => {}); // Silence errors in this fork of the Promise chain. const op = responseFilterOperation.chain((responseAndGotProgress) => { const responseFilterMs = Date.now() - responseFilterStartTime; const response = responseAndGotProgress.response; response.timeMs += requestFilterMs; response.timeMs += responseFilterMs; if (!responseAndGotProgress.gotProgress && this.onProgressUpdated_ && !response.fromCache && request.method != 'HEAD' && type == shaka.net.NetworkingEngine.RequestType.SEGMENT) { const allowSwitch = this.allowSwitch_(context); this.onProgressUpdated_( response.timeMs, response.data.byteLength, allowSwitch); } if (this.onResponse_) { this.onResponse_(type, response, context); } return response; }, (e) => { // Any error thrown from elsewhere should be recategorized as CRITICAL // here. This is because by the time it gets here, we've exhausted // retries. if (e) { goog.asserts.assert(e instanceof shaka.util.Error, 'Wrong error type'); e.severity = shaka.util.Error.Severity.CRITICAL; } throw e; }); // Return the pending request, which carries the response operation, and the // number of bytes remaining to be downloaded, updated by the progress // events. Add the operation to the manager for later cleanup. const pendingRequest = new shaka.net.NetworkingEngine.PendingRequest( op.promise, () => op.abort(), numBytesRemainingObj); this.operationManager_.manage(pendingRequest); return pendingRequest; } /** * @param {shaka.net.NetworkingEngine.RequestType} type * @param {shaka.extern.Request} request * @param {shaka.extern.RequestContext=} context * @return {!shaka.util.AbortableOperation.<undefined>} * @private */ filterRequest_(type, request, context) { let filterOperation = shaka.util.AbortableOperation.completed(undefined); const applyFilter = (requestFilter) => { filterOperation = filterOperation.chain(() => { if (request.body) { // TODO: For v4.0 we should remove this or change to always pass a // Uint8Array. To make it easier for apps to write filters, it may be // better to always pass a Uint8Array so they know what they are // getting; but we shouldn't use ArrayBuffer since that would require // copying buffers if this is a partial view. request.body = shaka.util.BufferUtils.toArrayBuffer(request.body); } return requestFilter(type, request, context); }); }; if (this.onRequest_) { applyFilter(this.onRequest_); } for (const requestFilter of this.requestFilters_) { // Request filters are run sequentially. applyFilter(requestFilter); } // Catch any errors thrown by request filters, and substitute // them with a Shaka-native error. return filterOperation.chain(undefined, (e) => { if (e instanceof shaka.util.Error && e.code == shaka.util.Error.Code.OPERATION_ABORTED) { // Don't change anything if the operation was aborted. throw e; } throw new shaka.util.Error( shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.NETWORK, shaka.util.Error.Code.REQUEST_FILTER_ERROR, e); }); } /** * @param {shaka.net.NetworkingEngine.RequestType} type * @param {shaka.extern.Request} request * @param {(shaka.extern.RequestContext|undefined)} context * @param {shaka.net.NetworkingEngine.NumBytesRemainingClass * } numBytesRemainingObj * @return {!shaka.extern.IAbortableOperation.< * shaka.net.NetworkingEngine.ResponseAndGotProgress>} * @private */ makeRequestWithRetry_(type, request, context, numBytesRemainingObj) { const backoff = new shaka.net.Backoff( request.retryParameters, /* autoReset= */ false); const index = 0; return this.send_( type, request, context, backoff, index, /* lastError= */ null, numBytesRemainingObj); } /** * Sends the given request to the correct plugin and retry using Backoff. * * @param {shaka.net.NetworkingEngine.RequestType} type * @param {shaka.extern.Request} request * @param {(shaka.extern.RequestContext|undefined)} context * @param {!shaka.net.Backoff} backoff * @param {number} index * @param {?shaka.util.Error} lastError * @param {shaka.net.NetworkingEngine.NumBytesRemainingClass * } numBytesRemainingObj * @return {!shaka.extern.IAbortableOperation.< * shaka.net.NetworkingEngine.ResponseAndGotProgress>} * @private */ send_(type, request, context, backoff, index, lastError, numBytesRemainingObj) { if (this.forceHTTP_) { request.uris[index] = request.uris[index].replace('https://', 'http://'); } if (this.forceHTTPS_) { request.uris[index] = request.uris[index].replace('http://', 'https://'); } if (index > 0 && this.onRetry_) { const newUri = request.uris[index]; const oldUri = request.uris[index - 1]; this.onRetry_(type, context, newUri, oldUri); } const uri = new goog.Uri(request.uris[index]); let scheme = uri.getScheme(); // Whether it got a progress event. let gotProgress = false; if (!scheme) { // If there is no scheme, infer one from the location. scheme = shaka.net.NetworkingEngine.getLocationProtocol_(); goog.asserts.assert( scheme[scheme.length - 1] == ':', 'location.protocol expected to end with a colon!'); // Drop the colon. scheme = scheme.slice(0, -1); // Override the original URI to make the scheme explicit. uri.setScheme(scheme); request.uris[index] = uri.toString(); } // Schemes are meant to be case-insensitive. // See https://github.com/shaka-project/shaka-player/issues/2173 // and https://tools.ietf.org/html/rfc3986#section-3.1 scheme = scheme.toLowerCase(); const object = shaka.net.NetworkingEngine.schemes_.get(scheme); const plugin = object ? object.plugin : null; if (!plugin) { return shaka.util.AbortableOperation.failed( new shaka.util.Error( shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.NETWORK, shaka.util.Error.Code.UNSUPPORTED_SCHEME, uri)); } const progressSupport = object.progressSupport; // Add headers from CommonAccessToken const commonAccessToken = this.hostCommonAccessTokenMap_.get(uri.getDomain()); if (commonAccessToken) { request.headers[shaka.net.NetworkingEngine.CommonAccessTokenHeaderName_] = commonAccessToken; } // Every attempt must have an associated backoff.attempt() call so that the // accounting is correct. const backoffOperation = shaka.util.AbortableOperation.notAbortable(backoff.attempt()); /** @type {?shaka.util.Timer} */ let connectionTimer = null; /** @type {?shaka.util.Timer} */ let stallTimer = null; let aborted = false; let headersReceivedCalled = false; let startTimeMs; const sendOperation = backoffOperation.chain(() => { if (this.destroyed_) { return shaka.util.AbortableOperation.aborted(); } startTimeMs = Date.now(); const segment = shaka.net.NetworkingEngine.RequestType.SEGMENT; let packetNumber = 0; const progressUpdated = (time, bytes, numBytesRemaining) => { if (connectionTimer) { connectionTimer.stop(); } if (stallTimer) { stallTimer.tickAfter(stallTimeoutMs / 1000); } if (this.onProgressUpdated_ && type == segment) { packetNumber++; request.packetNumber = packetNumber; const allowSwitch = this.allowSwitch_(context); this.onProgressUpdated_(time, bytes, allowSwitch, request); gotProgress = true; numBytesRemainingObj.setBytes(numBytesRemaining); } }; const headersReceived = (headers) => { headersReceivedCalled = true; request.timeToFirstByte = Date.now() - /** @type {number} */ (request.requestStartTime); if (this.onHeadersReceived_) { this.onHeadersReceived_(headers, request, type); } }; request.requestStartTime = Date.now(); const requestPlugin = plugin( request.uris[index], request, type, progressUpdated, headersReceived, { minBytesForProgressEvents: this.minBytesForProgressEvents_, }); if (!progressSupport) { return requestPlugin; } const connectionTimeoutMs = request.retryParameters.connectionTimeout; if (connectionTimeoutMs) { connectionTimer = new shaka.util.Timer(() => { aborted = true; requestPlugin.abort(); }); connectionTimer.tickAfter(connectionTimeoutMs / 1000); } const stallTimeoutMs = request.retryParameters.stallTimeout; if (stallTimeoutMs) { stallTimer = new shaka.util.Timer(() => { aborted = true; requestPlugin.abort(); }); } return requestPlugin; }).chain((response) => { if (connectionTimer) { connectionTimer.stop(); } if (stallTimer) { stallTimer.stop(); } if (response.timeMs == undefined) { response.timeMs = Date.now() - startTimeMs; } // Process headers to get the new CommonAccessToken const commonAccessTokenHeader = response.headers[ shaka.net.NetworkingEngine.CommonAccessTokenHeaderName_]; if (commonAccessTokenHeader) { const responseHost = new goog.Uri(response.uri); this.hostCommonAccessTokenMap_.set( responseHost.getDomain(), commonAccessTokenHeader); } const responseAndGotProgress = { response: response, gotProgress: gotProgress, }; if (!headersReceivedCalled) { // The plugin did not call headersReceived, perhaps because it is not // able to track that information. So, fire the event manually. if (this.onHeadersReceived_) { this.onHeadersReceived_(response.headers, request, type); } } if (this.onDownloadCompleted_) { this.onDownloadCompleted_(request, response); } return responseAndGotProgress; }, (error) => { if (connectionTimer) { connectionTimer.stop(); } if (stallTimer) { stallTimer.stop(); } if (this.onDownloadFailed_) { let shakaError = null; let httpResponseCode = 0; if (error instanceof shaka.util.Error) { shakaError = error; if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) { httpResponseCode = /** @type {number} */ (error.data[1]); } } this.onDownloadFailed_(request, shakaError, httpResponseCode, aborted); } if (this.destroyed_) { return shaka.util.AbortableOperation.aborted(); } if (aborted) { // It is necessary to change the error code to the correct one because // otherwise the retry logic would not work. error = new shaka.util.Error( shaka.util.Error.Severity.RECOVERABLE, shaka.util.Error.Category.NETWORK, shaka.util.Error.Code.TIMEOUT, request.uris[index], type); } if (error instanceof shaka.util.Error) { if (error.code == shaka.util.Error.Code.OPERATION_ABORTED) { // Don't change anything if the operation was aborted. throw error; } else if (error.code == shaka.util.Error.Code.ATTEMPTS_EXHAUSTED) { goog.asserts.assert(lastError, 'Should have last error'); throw lastError; } if (error.severity == shaka.util.Error.Severity.RECOVERABLE) { const data = (new Map()).set('error', error); const event = new shaka.util.FakeEvent('retry', data); // A user can call preventDefault() on a cancelable event. event.cancelable = true; this.dispatchEvent(event); if (event.defaultPrevented) { // If the caller uses preventDefault() on the 'retry' event, don't // retry any more. throw error; } // Move to the next URI. index = (index + 1) % request.uris.length; return this.send_( type, request, context, backoff, index, error, numBytesRemainingObj); } } // The error was not recoverable, so do not try again. throw error; }); return sendOperation; } /** * @param {shaka.net.NetworkingEngine.RequestType} type * @param {shaka.net.NetworkingEngine.ResponseAndGotProgress * } responseAndGotProgress * @param {shaka.extern.RequestContext=} context * @return {!shaka.extern.IAbortableOperation.< * shaka.net.NetworkingEngine.ResponseAndGotProgress>} * @private */ filterResponse_(type, responseAndGotProgress, context) { let filterOperation = shaka.util.AbortableOperation.completed(undefined); for (const responseFilter of this.responseFilters_) { // Response filters are run sequentially. filterOperation = filterOperation.chain(() => { const resp = responseAndGotProgress.response; if (resp.data) { // TODO: See TODO in filterRequest_. resp.data = shaka.util.BufferUtils.toArrayBuffer(resp.data); } return responseFilter(type, resp, context); }); } // If successful, return the filtered response with whether it got // progress. return filterOperation.chain(() => { return responseAndGotProgress; }, (e) => { // Catch any errors thrown by request filters, and substitute // them with a Shaka-native error. // The error is assumed to be critical if the original wasn't a Shaka // error. let severity = shaka.util.Error.Severity.CRITICAL; if (e instanceof shaka.util.Error) { if (e.code == shaka.util.Error.Code.OPERATION_ABORTED) { // Don't change anything if the operation was aborted. throw e; } severity = e.severity; } throw new shaka.util.Error( severity, shaka.util.Error.Category.NETWORK, shaka.util.Error.Code.RESPONSE_FILTER_ERROR, e); }); } /** * @param {(shaka.extern.RequestContext|undefined)} context * @return {boolean} * @private */ allowSwitch_(context) { if (context) { const segment = context.segment; const stream = context.stream; if (segment && stream && stream.fastSwitching) { if (segment.isPartial()) { return false; } } } return true; } /** * This is here only for testability. We can't mock location in our tests on * all browsers, so instead we mock this. * * @return {string} The value of location.protocol. * @private */ static getLocationProtocol_() { return location.protocol; } }; /** * A wrapper class for the number of bytes remaining to be downloaded for the * request. * Instead of using PendingRequest directly, this class is needed to be sent to * plugin as a parameter, and a Promise is returned, before PendingRequest is * created. * * @export */ shaka.net.NetworkingEngine.NumBytesRemainingClass = class { /** * Constructor */ constructor() { /** @private {number} */ this.bytesToLoad_ = 0; } /** * @param {number} bytesToLoad */ setBytes(bytesToLoad) { this.bytesToLoad_ = bytesToLoad; } /** * @return {number} */ getBytes() { return this.bytesToLoad_; } }; /** * A pending network request. This can track the current progress of the * download, and allows the request to be aborted if the network is slow. * * @implements {shaka.extern.IAbortableOperation.<shaka.extern.Response>} * @extends {shaka.util.AbortableOperation} * @export */ shaka.net.NetworkingEngine.PendingRequest = class extends shaka.util.AbortableOperation { /** * @param {!Promise} promise * A Promise which represents the underlying operation. It is resolved * when the operation is complete, and rejected if the operation fails or * is aborted. Aborted operations should be rejected with a * shaka.util.Error object using the error code OPERATION_ABORTED. * @param {function():!Promise} onAbort * Will be called by this object to abort the underlying operation. This * is not cancellation, and will not necessarily result in any work being * undone. abort() should return a Promise which is resolved when the * underlying operation has been aborted. The returned Promise should * never be rejected. * @param {shaka.net.NetworkingEngine.NumBytesRemainingClass * } numBytesRemainingObj */ constructor(promise, onAbort, numBytesRemainingObj) { super(promise, onAbort); /** @private {shaka.net.NetworkingEngine.NumBytesRemainingClass} */ this.bytesRemaining_ = numBytesRemainingObj; } /** * @return {number} */ getBytesRemaining() { return this.bytesRemaining_.getBytes(); } }; /** * @const {string} * @private */ shaka.net.NetworkingEngine.CommonAccessTokenHeaderName_ = 'common-access-token'; /** * Request types. Allows a filter to decide which requests to read/alter. * * @enum {number} * @export */ shaka.net.NetworkingEngine.RequestType = { 'MANIFEST': 0, 'SEGMENT': 1, 'LICENSE': 2, 'APP': 3, 'TIMING': 4, 'SERVER_CERTIFICATE': 5, 'KEY': 6, 'ADS': 7, 'CONTENT_STEERING': 8, }; /** * A more advanced form of the RequestType structure, meant to describe * sub-types of basic request types. * For example, an INIT_SEGMENT is a sub-type of SEGMENT. * This is meant to allow for more specificity to be added to the request type * data, without breaking backwards compatibility. * * @enum {number} * @export */ shaka.net.NetworkingEngine.AdvancedRequestType = { 'INIT_SEGMENT': 0, 'MEDIA_SEGMENT': 1, 'MEDIA_PLAYLIST': 2, 'MASTER_PLAYLIST': 3, 'MPD': 4, 'MSS': 5, 'MPD_PATCH': 6, 'MEDIATAILOR_SESSION_INFO': 7, 'MEDIATAILOR_TRACKING_INFO': 8, 'MEDIATAILOR_STATIC_RESOURCE': 9, 'MEDIATAILOR_TRACKING_EVENT': 10, 'INTERSTITIAL_ASSET_LIST': 11, 'INTERSTITIAL_AD_URL': 12, }; /** * Priority level for network scheme plugins. * If multiple plugins are provided for the same scheme, only the * highest-priority one is used. * * @enum {number} * @export */ shaka.net.NetworkingEngine.PluginPriority = { 'FALLBACK': 1, 'PREFERRED': 2, 'APPLICATION': 3, }; /** * @typedef {{ * plugin: shaka.extern.SchemePlugin, * priority: number, * progressSupport: boolean * }} * @property {shaka.extern.SchemePlugin} plugin * The associated plugin. * @property {number} priority * The plugin's priority. * @property {boolean} progressSupport * The plugin's supports progress events */ shaka.net.NetworkingEngine.SchemeObject; /** * Contains the scheme plugins. * * @private {!Map<string, shaka.net.NetworkingEngine.SchemeObject>} */ shaka.net.NetworkingEngine.schemes_ = new Map(); /** * @typedef {{ * response: shaka.extern.Response, * gotProgress: boolean * }} * * @description * Defines a response wrapper object, including the response object and whether * progress event is fired by the scheme plugin. * * @property {shaka.extern.Response} response * @property {boolean} gotProgress * @private */ shaka.net.NetworkingEngine.ResponseAndGotProgress; /** * @typedef {function( * !Object<string, string>, * !shaka.extern.Request, * !shaka.net.NetworkingEngine.RequestType)} * * @description * A callback function that passes the shaka.extern.HeadersReceived along to * the player, plus some extra data. * @export */ shaka.net.NetworkingEngine.OnHeadersReceived; /** * @typedef {function( * number, * number, * boolean, * shaka.extern.Request=)} * * @description * A callback that is passed the duration, in milliseconds, * that the request took, the number of bytes transferred, a boolean * representing whether the switching is allowed and a ref to the * original request. * @export */ shaka.net.NetworkingEngine.onProgressUpdated; /** * @typedef {function( * !shaka.extern.Request, * !shaka.extern.Response)} * * @description * A callback function that notifies the player when a download completed * successfully. * @export */ shaka.net.NetworkingEngine.OnDownloadCompleted; /** * @typedef {function( * !shaka.extern.Request, * ?shaka.util.Error, * number, * boolean)} * * @description * A callback function that notifies the player when a download fails, for any * reason (e.g. even if the download was aborted). * @export */ shaka.net.NetworkingEngine.OnDownloadFailed; /** * @typedef {function( * !shaka.net.NetworkingEngine.RequestType, * !shaka.extern.Request, * (shaka.extern.RequestContext|undefined))} * * @description * A callback function called on every request * @export */ shaka.net.NetworkingEngine.OnRequest; /** * @typedef {function( * !shaka.net.NetworkingEngine.RequestType, * (shaka.extern.RequestContext|undefined), * string, * string)} * * @description * A callback function called on every request retry. The first string is the * new URI and the second string is the old URI. * @export */ shaka.net.NetworkingEngine.OnRetry; /** * @typedef {function( * !shaka.net.NetworkingEngine.RequestType, * !shaka.extern.Response, * (shaka.extern.RequestContext|undefined))} * * @description * A callback function called on every request * @export */ shaka.net.NetworkingEngine.OnResponse;