Source: lib/net/networking_engine.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.net.NetworkingEngine');
  7. goog.provide('shaka.net.NetworkingEngine.RequestType');
  8. goog.provide('shaka.net.NetworkingEngine.PendingRequest');
  9. goog.require('goog.Uri');
  10. goog.require('goog.asserts');
  11. goog.require('shaka.net.Backoff');
  12. goog.require('shaka.util.AbortableOperation');
  13. goog.require('shaka.util.BufferUtils');
  14. goog.require('shaka.util.Error');
  15. goog.require('shaka.util.FakeEvent');
  16. goog.require('shaka.util.FakeEventTarget');
  17. goog.require('shaka.util.IDestroyable');
  18. goog.require('shaka.util.ObjectUtils');
  19. goog.require('shaka.util.OperationManager');
  20. goog.require('shaka.util.Timer');
  21. /**
  22. * @event shaka.net.NetworkingEngine.RetryEvent
  23. * @description Fired when the networking engine receives a recoverable error
  24. * and retries.
  25. * @property {string} type
  26. * 'retry'
  27. * @property {?shaka.util.Error} error
  28. * The error that caused the retry. If it was a non-Shaka error, this is set
  29. * to null.
  30. * @exportDoc
  31. */
  32. /**
  33. * NetworkingEngine wraps all networking operations. This accepts plugins that
  34. * handle the actual request. A plugin is registered using registerScheme.
  35. * Each scheme has at most one plugin to handle the request.
  36. *
  37. * @implements {shaka.util.IDestroyable}
  38. * @export
  39. */
  40. shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
  41. /**
  42. * @param {function(number, number)=} onProgressUpdated Called when a progress
  43. * event is triggered. Passed the duration, in milliseconds, that the
  44. * request took, and the number of bytes transferred.
  45. * @param {shaka.net.NetworkingEngine.OnHeadersReceived=} onHeadersReceived
  46. * Called when the headers are received for a download.
  47. * @param {shaka.net.NetworkingEngine.OnDownloadFailed=} onDownloadFailed
  48. * Called when a download fails, for any reason.
  49. */
  50. constructor(onProgressUpdated, onHeadersReceived, onDownloadFailed) {
  51. super();
  52. /** @private {boolean} */
  53. this.destroyed_ = false;
  54. /** @private {!shaka.util.OperationManager} */
  55. this.operationManager_ = new shaka.util.OperationManager();
  56. /** @private {!Set.<shaka.extern.RequestFilter>} */
  57. this.requestFilters_ = new Set();
  58. /** @private {!Set.<shaka.extern.ResponseFilter>} */
  59. this.responseFilters_ = new Set();
  60. /** @private {?function(number, number)} */
  61. this.onProgressUpdated_ = onProgressUpdated || null;
  62. /** @private {?shaka.net.NetworkingEngine.OnHeadersReceived} */
  63. this.onHeadersReceived_ = onHeadersReceived || null;
  64. /** @private {?shaka.net.NetworkingEngine.OnDownloadFailed} */
  65. this.onDownloadFailed_ = onDownloadFailed || null;
  66. /** @private {boolean} */
  67. this.forceHTTPS_ = false;
  68. }
  69. /**
  70. * @param {boolean} forceHTTPS
  71. * @export
  72. */
  73. setForceHTTPS(forceHTTPS) {
  74. this.forceHTTPS_ = forceHTTPS;
  75. }
  76. /**
  77. * Registers a scheme plugin. This plugin will handle all requests with the
  78. * given scheme. If a plugin with the same scheme already exists, it is
  79. * replaced, unless the existing plugin is of higher priority.
  80. * If no priority is provided, this defaults to the highest priority of
  81. * APPLICATION.
  82. *
  83. * @param {string} scheme
  84. * @param {shaka.extern.SchemePlugin} plugin
  85. * @param {number=} priority
  86. * @param {boolean=} progressSupport
  87. * @export
  88. */
  89. static registerScheme(scheme, plugin, priority, progressSupport = false) {
  90. goog.asserts.assert(
  91. priority == undefined || priority > 0, 'explicit priority must be > 0');
  92. priority =
  93. priority || shaka.net.NetworkingEngine.PluginPriority.APPLICATION;
  94. const existing = shaka.net.NetworkingEngine.schemes_[scheme];
  95. if (!existing || priority >= existing.priority) {
  96. shaka.net.NetworkingEngine.schemes_[scheme] = {
  97. priority: priority,
  98. plugin: plugin,
  99. progressSupport: progressSupport,
  100. };
  101. }
  102. }
  103. /**
  104. * Removes a scheme plugin.
  105. *
  106. * @param {string} scheme
  107. * @export
  108. */
  109. static unregisterScheme(scheme) {
  110. delete shaka.net.NetworkingEngine.schemes_[scheme];
  111. }
  112. /**
  113. * Registers a new request filter. All filters are applied in the order they
  114. * are registered.
  115. *
  116. * @param {shaka.extern.RequestFilter} filter
  117. * @export
  118. */
  119. registerRequestFilter(filter) {
  120. this.requestFilters_.add(filter);
  121. }
  122. /**
  123. * Removes a request filter.
  124. *
  125. * @param {shaka.extern.RequestFilter} filter
  126. * @export
  127. */
  128. unregisterRequestFilter(filter) {
  129. this.requestFilters_.delete(filter);
  130. }
  131. /**
  132. * Clears all request filters.
  133. *
  134. * @export
  135. */
  136. clearAllRequestFilters() {
  137. this.requestFilters_.clear();
  138. }
  139. /**
  140. * Registers a new response filter. All filters are applied in the order they
  141. * are registered.
  142. *
  143. * @param {shaka.extern.ResponseFilter} filter
  144. * @export
  145. */
  146. registerResponseFilter(filter) {
  147. this.responseFilters_.add(filter);
  148. }
  149. /**
  150. * Removes a response filter.
  151. *
  152. * @param {shaka.extern.ResponseFilter} filter
  153. * @export
  154. */
  155. unregisterResponseFilter(filter) {
  156. this.responseFilters_.delete(filter);
  157. }
  158. /**
  159. * Clears all response filters.
  160. *
  161. * @export
  162. */
  163. clearAllResponseFilters() {
  164. this.responseFilters_.clear();
  165. }
  166. /**
  167. * Gets a copy of the default retry parameters.
  168. *
  169. * @return {shaka.extern.RetryParameters}
  170. *
  171. * NOTE: The implementation moved to shaka.net.Backoff to avoid a circular
  172. * dependency between the two classes.
  173. *
  174. * @export
  175. */
  176. static defaultRetryParameters() {
  177. return shaka.net.Backoff.defaultRetryParameters();
  178. }
  179. /**
  180. * Makes a simple network request for the given URIs.
  181. *
  182. * @param {!Array.<string>} uris
  183. * @param {shaka.extern.RetryParameters} retryParams
  184. * @param {?function(BufferSource):!Promise=} streamDataCallback
  185. * @return {shaka.extern.Request}
  186. * @export
  187. */
  188. static makeRequest(uris, retryParams, streamDataCallback = null) {
  189. return {
  190. uris: uris,
  191. method: 'GET',
  192. body: null,
  193. headers: {},
  194. allowCrossSiteCredentials: false,
  195. retryParameters: retryParams,
  196. licenseRequestType: null,
  197. sessionId: null,
  198. streamDataCallback: streamDataCallback,
  199. };
  200. }
  201. /**
  202. * @override
  203. * @export
  204. */
  205. destroy() {
  206. this.destroyed_ = true;
  207. this.requestFilters_.clear();
  208. this.responseFilters_.clear();
  209. // FakeEventTarget implements IReleasable
  210. super.release();
  211. return this.operationManager_.destroy();
  212. }
  213. /**
  214. * Makes a network request and returns the resulting data.
  215. *
  216. * @param {shaka.net.NetworkingEngine.RequestType} type
  217. * @param {shaka.extern.Request} request
  218. * @return {!shaka.net.NetworkingEngine.PendingRequest}
  219. * @export
  220. */
  221. request(type, request) {
  222. const ObjectUtils = shaka.util.ObjectUtils;
  223. const numBytesRemainingObj =
  224. new shaka.net.NetworkingEngine.NumBytesRemainingClass();
  225. // Reject all requests made after destroy is called.
  226. if (this.destroyed_) {
  227. const p = Promise.reject(new shaka.util.Error(
  228. shaka.util.Error.Severity.CRITICAL,
  229. shaka.util.Error.Category.PLAYER,
  230. shaka.util.Error.Code.OPERATION_ABORTED));
  231. // Silence uncaught rejection errors, which may otherwise occur any place
  232. // we don't explicitly handle aborted operations.
  233. p.catch(() => {});
  234. return new shaka.net.NetworkingEngine.PendingRequest(
  235. p, () => Promise.resolve(), numBytesRemainingObj);
  236. }
  237. goog.asserts.assert(
  238. request.uris && request.uris.length, 'Request without URIs!');
  239. // If a request comes from outside the library, some parameters may be left
  240. // undefined. To make it easier for application developers, we will fill
  241. // them in with defaults if necessary.
  242. //
  243. // We clone retryParameters and uris so that if a filter modifies the
  244. // request, it doesn't contaminate future requests.
  245. request.method = request.method || 'GET';
  246. request.headers = request.headers || {};
  247. request.retryParameters = request.retryParameters ?
  248. ObjectUtils.cloneObject(request.retryParameters) :
  249. shaka.net.NetworkingEngine.defaultRetryParameters();
  250. request.uris = ObjectUtils.cloneObject(request.uris);
  251. // Apply the registered filters to the request.
  252. const requestFilterOperation = this.filterRequest_(type, request);
  253. const requestOperation = requestFilterOperation.chain(
  254. () => this.makeRequestWithRetry_(type, request, numBytesRemainingObj));
  255. const responseFilterOperation = requestOperation.chain(
  256. (responseAndGotProgress) =>
  257. this.filterResponse_(type, responseAndGotProgress));
  258. // Keep track of time spent in filters.
  259. const requestFilterStartTime = Date.now();
  260. let requestFilterMs = 0;
  261. requestFilterOperation.promise.then(() => {
  262. requestFilterMs = Date.now() - requestFilterStartTime;
  263. }, () => {}); // Silence errors in this fork of the Promise chain.
  264. let responseFilterStartTime = 0;
  265. requestOperation.promise.then(() => {
  266. responseFilterStartTime = Date.now();
  267. }, () => {}); // Silence errors in this fork of the Promise chain.
  268. const op = responseFilterOperation.chain((responseAndGotProgress) => {
  269. const responseFilterMs = Date.now() - responseFilterStartTime;
  270. const response = responseAndGotProgress.response;
  271. response.timeMs += requestFilterMs;
  272. response.timeMs += responseFilterMs;
  273. if (!responseAndGotProgress.gotProgress &&
  274. this.onProgressUpdated_ &&
  275. !response.fromCache &&
  276. request.method != 'HEAD' &&
  277. type == shaka.net.NetworkingEngine.RequestType.SEGMENT) {
  278. this.onProgressUpdated_(response.timeMs, response.data.byteLength);
  279. }
  280. return response;
  281. }, (e) => {
  282. // Any error thrown from elsewhere should be recategorized as CRITICAL
  283. // here. This is because by the time it gets here, we've exhausted
  284. // retries.
  285. if (e) {
  286. goog.asserts.assert(e instanceof shaka.util.Error, 'Wrong error type');
  287. e.severity = shaka.util.Error.Severity.CRITICAL;
  288. }
  289. throw e;
  290. });
  291. // Return the pending request, which carries the response operation, and the
  292. // number of bytes remaining to be downloaded, updated by the progress
  293. // events. Add the operation to the manager for later cleanup.
  294. const pendingRequest =
  295. new shaka.net.NetworkingEngine.PendingRequest(
  296. op.promise, () => op.abort(), numBytesRemainingObj);
  297. this.operationManager_.manage(pendingRequest);
  298. return pendingRequest;
  299. }
  300. /**
  301. * @param {shaka.net.NetworkingEngine.RequestType} type
  302. * @param {shaka.extern.Request} request
  303. * @return {!shaka.util.AbortableOperation.<undefined>}
  304. * @private
  305. */
  306. filterRequest_(type, request) {
  307. let filterOperation = shaka.util.AbortableOperation.completed(undefined);
  308. for (const requestFilter of this.requestFilters_) {
  309. // Request filters are run sequentially.
  310. filterOperation = filterOperation.chain(() => {
  311. if (request.body) {
  312. // TODO: For v4.0 we should remove this or change to always pass a
  313. // Uint8Array. To make it easier for apps to write filters, it may be
  314. // better to always pass a Uint8Array so they know what they are
  315. // getting; but we shouldn't use ArrayBuffer since that would require
  316. // copying buffers if this is a partial view.
  317. request.body = shaka.util.BufferUtils.toArrayBuffer(request.body);
  318. }
  319. return requestFilter(type, request);
  320. });
  321. }
  322. // Catch any errors thrown by request filters, and substitute
  323. // them with a Shaka-native error.
  324. return filterOperation.chain(undefined, (e) => {
  325. if (e instanceof shaka.util.Error &&
  326. e.code == shaka.util.Error.Code.OPERATION_ABORTED) {
  327. // Don't change anything if the operation was aborted.
  328. throw e;
  329. }
  330. throw new shaka.util.Error(
  331. shaka.util.Error.Severity.CRITICAL,
  332. shaka.util.Error.Category.NETWORK,
  333. shaka.util.Error.Code.REQUEST_FILTER_ERROR, e);
  334. });
  335. }
  336. /**
  337. * @param {shaka.net.NetworkingEngine.RequestType} type
  338. * @param {shaka.extern.Request} request
  339. * @param {shaka.net.NetworkingEngine.NumBytesRemainingClass}
  340. * numBytesRemainingObj
  341. * @return {!shaka.extern.IAbortableOperation.<
  342. * shaka.net.NetworkingEngine.ResponseAndGotProgress>}
  343. * @private
  344. */
  345. makeRequestWithRetry_(type, request, numBytesRemainingObj) {
  346. const backoff = new shaka.net.Backoff(
  347. request.retryParameters, /* autoReset= */ false);
  348. const index = 0;
  349. return this.send_(
  350. type, request, backoff, index, /* lastError= */ null,
  351. numBytesRemainingObj);
  352. }
  353. /**
  354. * Sends the given request to the correct plugin and retry using Backoff.
  355. *
  356. * @param {shaka.net.NetworkingEngine.RequestType} type
  357. * @param {shaka.extern.Request} request
  358. * @param {!shaka.net.Backoff} backoff
  359. * @param {number} index
  360. * @param {?shaka.util.Error} lastError
  361. * @param {shaka.net.NetworkingEngine.NumBytesRemainingClass}
  362. * numBytesRemainingObj
  363. * @return {!shaka.extern.IAbortableOperation.<
  364. * shaka.net.NetworkingEngine.ResponseAndGotProgress>}
  365. * @private
  366. */
  367. send_(type, request, backoff, index, lastError, numBytesRemainingObj) {
  368. if (this.forceHTTPS_) {
  369. request.uris[index] = request.uris[index].replace('http://', 'https://');
  370. }
  371. const uri = new goog.Uri(request.uris[index]);
  372. let scheme = uri.getScheme();
  373. // Whether it got a progress event.
  374. let gotProgress = false;
  375. if (!scheme) {
  376. // If there is no scheme, infer one from the location.
  377. scheme = shaka.net.NetworkingEngine.getLocationProtocol_();
  378. goog.asserts.assert(
  379. scheme[scheme.length - 1] == ':',
  380. 'location.protocol expected to end with a colon!');
  381. // Drop the colon.
  382. scheme = scheme.slice(0, -1);
  383. // Override the original URI to make the scheme explicit.
  384. uri.setScheme(scheme);
  385. request.uris[index] = uri.toString();
  386. }
  387. // Schemes are meant to be case-insensitive.
  388. // See https://github.com/shaka-project/shaka-player/issues/2173
  389. // and https://tools.ietf.org/html/rfc3986#section-3.1
  390. scheme = scheme.toLowerCase();
  391. const object = shaka.net.NetworkingEngine.schemes_[scheme];
  392. const plugin = object ? object.plugin : null;
  393. if (!plugin) {
  394. return shaka.util.AbortableOperation.failed(
  395. new shaka.util.Error(
  396. shaka.util.Error.Severity.CRITICAL,
  397. shaka.util.Error.Category.NETWORK,
  398. shaka.util.Error.Code.UNSUPPORTED_SCHEME,
  399. uri));
  400. }
  401. const progressSupport = object.progressSupport;
  402. // Every attempt must have an associated backoff.attempt() call so that the
  403. // accounting is correct.
  404. const backoffOperation =
  405. shaka.util.AbortableOperation.notAbortable(backoff.attempt());
  406. /** @type {?shaka.util.Timer} */
  407. let connectionTimer = null;
  408. /** @type {?shaka.util.Timer} */
  409. let stallTimer = null;
  410. let aborted = false;
  411. let headersReceivedCalled = false;
  412. let startTimeMs;
  413. const sendOperation = backoffOperation.chain(() => {
  414. if (this.destroyed_) {
  415. return shaka.util.AbortableOperation.aborted();
  416. }
  417. startTimeMs = Date.now();
  418. const segment = shaka.net.NetworkingEngine.RequestType.SEGMENT;
  419. const progressUpdated = (time, bytes, numBytesRemaining) => {
  420. if (connectionTimer) {
  421. connectionTimer.stop();
  422. }
  423. if (stallTimer) {
  424. stallTimer.tickAfter(stallTimeoutMs / 1000);
  425. }
  426. if (this.onProgressUpdated_ && type == segment) {
  427. this.onProgressUpdated_(time, bytes);
  428. gotProgress = true;
  429. numBytesRemainingObj.setBytes(numBytesRemaining);
  430. }
  431. };
  432. const headersReceived = (headers) => {
  433. if (this.onHeadersReceived_) {
  434. this.onHeadersReceived_(headers, request, type);
  435. }
  436. headersReceivedCalled = true;
  437. };
  438. const requestPlugin = plugin(
  439. request.uris[index], request, type, progressUpdated, headersReceived);
  440. if (!progressSupport) {
  441. return requestPlugin;
  442. }
  443. const connectionTimeoutMs = request.retryParameters.connectionTimeout;
  444. if (connectionTimeoutMs) {
  445. connectionTimer = new shaka.util.Timer(() => {
  446. aborted = true;
  447. requestPlugin.abort();
  448. });
  449. connectionTimer.tickAfter(connectionTimeoutMs / 1000);
  450. }
  451. const stallTimeoutMs = request.retryParameters.stallTimeout;
  452. if (stallTimeoutMs) {
  453. stallTimer = new shaka.util.Timer(() => {
  454. aborted = true;
  455. requestPlugin.abort();
  456. });
  457. }
  458. return requestPlugin;
  459. }).chain((response) => {
  460. if (connectionTimer) {
  461. connectionTimer.stop();
  462. }
  463. if (stallTimer) {
  464. stallTimer.stop();
  465. }
  466. if (response.timeMs == undefined) {
  467. response.timeMs = Date.now() - startTimeMs;
  468. }
  469. const responseAndGotProgress = {
  470. response: response,
  471. gotProgress: gotProgress,
  472. };
  473. if (!headersReceivedCalled) {
  474. // The plugin did not call headersReceived, perhaps because it is not
  475. // able to track that information. So, fire the event manually.
  476. if (this.onHeadersReceived_) {
  477. this.onHeadersReceived_(response.headers, request, type);
  478. }
  479. }
  480. return responseAndGotProgress;
  481. }, (error) => {
  482. if (connectionTimer) {
  483. connectionTimer.stop();
  484. }
  485. if (stallTimer) {
  486. stallTimer.stop();
  487. }
  488. if (this.onDownloadFailed_) {
  489. let shakaError = null;
  490. let httpResponseCode = 0;
  491. if (error instanceof shaka.util.Error) {
  492. shakaError = error;
  493. if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) {
  494. httpResponseCode = /** @type {number} */ (error.data[1]);
  495. }
  496. }
  497. this.onDownloadFailed_(request, shakaError, httpResponseCode, aborted);
  498. }
  499. if (this.destroyed_) {
  500. return shaka.util.AbortableOperation.aborted();
  501. }
  502. if (aborted) {
  503. // It is necessary to change the error code to the correct one because
  504. // otherwise the retry logic would not work.
  505. error = new shaka.util.Error(
  506. shaka.util.Error.Severity.RECOVERABLE,
  507. shaka.util.Error.Category.NETWORK,
  508. shaka.util.Error.Code.TIMEOUT,
  509. request.uris[index], type);
  510. }
  511. if (error instanceof shaka.util.Error) {
  512. if (error.code == shaka.util.Error.Code.OPERATION_ABORTED) {
  513. // Don't change anything if the operation was aborted.
  514. throw error;
  515. } else if (error.code == shaka.util.Error.Code.ATTEMPTS_EXHAUSTED) {
  516. goog.asserts.assert(lastError, 'Should have last error');
  517. throw lastError;
  518. }
  519. if (error.severity == shaka.util.Error.Severity.RECOVERABLE) {
  520. const data = (new Map()).set('error', error);
  521. const event = new shaka.util.FakeEvent('retry', data);
  522. this.dispatchEvent(event);
  523. // Move to the next URI.
  524. index = (index + 1) % request.uris.length;
  525. return this.send_(
  526. type, request, backoff, index, error, numBytesRemainingObj);
  527. }
  528. }
  529. // The error was not recoverable, so do not try again.
  530. throw error;
  531. });
  532. return sendOperation;
  533. }
  534. /**
  535. * @param {shaka.net.NetworkingEngine.RequestType} type
  536. * @param {shaka.net.NetworkingEngine.ResponseAndGotProgress}
  537. * responseAndGotProgress
  538. * @return {!shaka.extern.IAbortableOperation.<
  539. * shaka.net.NetworkingEngine.ResponseAndGotProgress>}
  540. * @private
  541. */
  542. filterResponse_(type, responseAndGotProgress) {
  543. let filterOperation = shaka.util.AbortableOperation.completed(undefined);
  544. for (const responseFilter of this.responseFilters_) {
  545. // Response filters are run sequentially.
  546. filterOperation = filterOperation.chain(() => {
  547. const resp = responseAndGotProgress.response;
  548. if (resp.data) {
  549. // TODO: See TODO in filterRequest_.
  550. resp.data = shaka.util.BufferUtils.toArrayBuffer(resp.data);
  551. }
  552. return responseFilter(type, resp);
  553. });
  554. }
  555. // If successful, return the filtered response with whether it got
  556. // progress.
  557. return filterOperation.chain(() => {
  558. return responseAndGotProgress;
  559. }, (e) => {
  560. // Catch any errors thrown by request filters, and substitute
  561. // them with a Shaka-native error.
  562. // The error is assumed to be critical if the original wasn't a Shaka
  563. // error.
  564. let severity = shaka.util.Error.Severity.CRITICAL;
  565. if (e instanceof shaka.util.Error) {
  566. if (e.code == shaka.util.Error.Code.OPERATION_ABORTED) {
  567. // Don't change anything if the operation was aborted.
  568. throw e;
  569. }
  570. severity = e.severity;
  571. }
  572. throw new shaka.util.Error(
  573. severity,
  574. shaka.util.Error.Category.NETWORK,
  575. shaka.util.Error.Code.RESPONSE_FILTER_ERROR, e);
  576. });
  577. }
  578. /**
  579. * This is here only for testability. We can't mock location in our tests on
  580. * all browsers, so instead we mock this.
  581. *
  582. * @return {string} The value of location.protocol.
  583. * @private
  584. */
  585. static getLocationProtocol_() {
  586. return location.protocol;
  587. }
  588. };
  589. /**
  590. * A wrapper class for the number of bytes remaining to be downloaded for the
  591. * request.
  592. * Instead of using PendingRequest directly, this class is needed to be sent to
  593. * plugin as a parameter, and a Promise is returned, before PendingRequest is
  594. * created.
  595. *
  596. * @export
  597. */
  598. shaka.net.NetworkingEngine.NumBytesRemainingClass = class {
  599. /**
  600. * Constructor
  601. */
  602. constructor() {
  603. /** @private {number} */
  604. this.bytesToLoad_ = 0;
  605. }
  606. /**
  607. * @param {number} bytesToLoad
  608. */
  609. setBytes(bytesToLoad) {
  610. this.bytesToLoad_ = bytesToLoad;
  611. }
  612. /**
  613. * @return {number}
  614. */
  615. getBytes() {
  616. return this.bytesToLoad_;
  617. }
  618. };
  619. /**
  620. * A pending network request. This can track the current progress of the
  621. * download, and allows the request to be aborted if the network is slow.
  622. *
  623. * @implements {shaka.extern.IAbortableOperation.<shaka.extern.Response>}
  624. * @extends {shaka.util.AbortableOperation}
  625. * @export
  626. */
  627. shaka.net.NetworkingEngine.PendingRequest =
  628. class extends shaka.util.AbortableOperation {
  629. /**
  630. * @param {!Promise} promise
  631. * A Promise which represents the underlying operation. It is resolved
  632. * when the operation is complete, and rejected if the operation fails or
  633. * is aborted. Aborted operations should be rejected with a
  634. * shaka.util.Error object using the error code OPERATION_ABORTED.
  635. * @param {function():!Promise} onAbort
  636. * Will be called by this object to abort the underlying operation. This
  637. * is not cancelation, and will not necessarily result in any work being
  638. * undone. abort() should return a Promise which is resolved when the
  639. * underlying operation has been aborted. The returned Promise should
  640. * never be rejected.
  641. * @param {shaka.net.NetworkingEngine.NumBytesRemainingClass}
  642. * numBytesRemainingObj
  643. */
  644. constructor(promise, onAbort, numBytesRemainingObj) {
  645. super(promise, onAbort);
  646. /** @private {shaka.net.NetworkingEngine.NumBytesRemainingClass} */
  647. this.bytesRemaining_ = numBytesRemainingObj;
  648. }
  649. /**
  650. * @return {number}
  651. */
  652. getBytesRemaining() {
  653. return this.bytesRemaining_.getBytes();
  654. }
  655. };
  656. /**
  657. * Request types. Allows a filter to decide which requests to read/alter.
  658. *
  659. * @enum {number}
  660. * @export
  661. */
  662. shaka.net.NetworkingEngine.RequestType = {
  663. 'MANIFEST': 0,
  664. 'SEGMENT': 1,
  665. 'LICENSE': 2,
  666. 'APP': 3,
  667. 'TIMING': 4,
  668. 'SERVER_CERTIFICATE': 5,
  669. };
  670. /**
  671. * Priority level for network scheme plugins.
  672. * If multiple plugins are provided for the same scheme, only the
  673. * highest-priority one is used.
  674. *
  675. * @enum {number}
  676. * @export
  677. */
  678. shaka.net.NetworkingEngine.PluginPriority = {
  679. 'FALLBACK': 1,
  680. 'PREFERRED': 2,
  681. 'APPLICATION': 3,
  682. };
  683. /**
  684. * @typedef {{
  685. * plugin: shaka.extern.SchemePlugin,
  686. * priority: number,
  687. * progressSupport: boolean
  688. * }}
  689. * @property {shaka.extern.SchemePlugin} plugin
  690. * The associated plugin.
  691. * @property {number} priority
  692. * The plugin's priority.
  693. * @property {boolean} progressSupport
  694. * The plugin's supports progress events
  695. */
  696. shaka.net.NetworkingEngine.SchemeObject;
  697. /**
  698. * Contains the scheme plugins.
  699. *
  700. * @private {!Object.<string, shaka.net.NetworkingEngine.SchemeObject>}
  701. */
  702. shaka.net.NetworkingEngine.schemes_ = {};
  703. /**
  704. * @typedef {{
  705. * response: shaka.extern.Response,
  706. * gotProgress: boolean
  707. * }}
  708. *
  709. * @description
  710. * Defines a response wrapper object, including the response object and whether
  711. * progress event is fired by the scheme plugin.
  712. *
  713. * @property {shaka.extern.Response} response
  714. * @property {boolean} gotProgress
  715. * @private
  716. */
  717. shaka.net.NetworkingEngine.ResponseAndGotProgress;
  718. /**
  719. * @typedef {function(
  720. * !Object.<string, string>,
  721. * !shaka.extern.Request,
  722. * !shaka.net.NetworkingEngine.RequestType)}
  723. *
  724. * @description
  725. * A callback function that passes the shaka.extern.HeadersReceived along to
  726. * the player, plus some extra data.
  727. * @export
  728. */
  729. shaka.net.NetworkingEngine.OnHeadersReceived;
  730. /**
  731. * @typedef {function(
  732. * !shaka.extern.Request,
  733. * ?shaka.util.Error,
  734. * number,
  735. * boolean)}
  736. *
  737. * @description
  738. * A callback function that notifies the player when a download fails, for any
  739. * reason (e.g. even if the download was aborted).
  740. * @export
  741. */
  742. shaka.net.NetworkingEngine.OnDownloadFailed;