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. drmInfo: null,
  199. initData: null,
  200. initDataType: null,
  201. streamDataCallback: streamDataCallback,
  202. };
  203. }
  204. /**
  205. * @override
  206. * @export
  207. */
  208. destroy() {
  209. this.destroyed_ = true;
  210. this.requestFilters_.clear();
  211. this.responseFilters_.clear();
  212. // FakeEventTarget implements IReleasable
  213. super.release();
  214. return this.operationManager_.destroy();
  215. }
  216. /**
  217. * Makes a network request and returns the resulting data.
  218. *
  219. * @param {shaka.net.NetworkingEngine.RequestType} type
  220. * @param {shaka.extern.Request} request
  221. * @return {!shaka.net.NetworkingEngine.PendingRequest}
  222. * @export
  223. */
  224. request(type, request) {
  225. const ObjectUtils = shaka.util.ObjectUtils;
  226. const numBytesRemainingObj =
  227. new shaka.net.NetworkingEngine.NumBytesRemainingClass();
  228. // Reject all requests made after destroy is called.
  229. if (this.destroyed_) {
  230. const p = Promise.reject(new shaka.util.Error(
  231. shaka.util.Error.Severity.CRITICAL,
  232. shaka.util.Error.Category.PLAYER,
  233. shaka.util.Error.Code.OPERATION_ABORTED));
  234. // Silence uncaught rejection errors, which may otherwise occur any place
  235. // we don't explicitly handle aborted operations.
  236. p.catch(() => {});
  237. return new shaka.net.NetworkingEngine.PendingRequest(
  238. p, () => Promise.resolve(), numBytesRemainingObj);
  239. }
  240. goog.asserts.assert(
  241. request.uris && request.uris.length, 'Request without URIs!');
  242. // If a request comes from outside the library, some parameters may be left
  243. // undefined. To make it easier for application developers, we will fill
  244. // them in with defaults if necessary.
  245. //
  246. // We clone retryParameters and uris so that if a filter modifies the
  247. // request, it doesn't contaminate future requests.
  248. request.method = request.method || 'GET';
  249. request.headers = request.headers || {};
  250. request.retryParameters = request.retryParameters ?
  251. ObjectUtils.cloneObject(request.retryParameters) :
  252. shaka.net.NetworkingEngine.defaultRetryParameters();
  253. request.uris = ObjectUtils.cloneObject(request.uris);
  254. // Apply the registered filters to the request.
  255. const requestFilterOperation = this.filterRequest_(type, request);
  256. const requestOperation = requestFilterOperation.chain(
  257. () => this.makeRequestWithRetry_(type, request, numBytesRemainingObj));
  258. const responseFilterOperation = requestOperation.chain(
  259. (responseAndGotProgress) =>
  260. this.filterResponse_(type, responseAndGotProgress));
  261. // Keep track of time spent in filters.
  262. const requestFilterStartTime = Date.now();
  263. let requestFilterMs = 0;
  264. requestFilterOperation.promise.then(() => {
  265. requestFilterMs = Date.now() - requestFilterStartTime;
  266. }, () => {}); // Silence errors in this fork of the Promise chain.
  267. let responseFilterStartTime = 0;
  268. requestOperation.promise.then(() => {
  269. responseFilterStartTime = Date.now();
  270. }, () => {}); // Silence errors in this fork of the Promise chain.
  271. const op = responseFilterOperation.chain((responseAndGotProgress) => {
  272. const responseFilterMs = Date.now() - responseFilterStartTime;
  273. const response = responseAndGotProgress.response;
  274. response.timeMs += requestFilterMs;
  275. response.timeMs += responseFilterMs;
  276. if (!responseAndGotProgress.gotProgress &&
  277. this.onProgressUpdated_ &&
  278. !response.fromCache &&
  279. type == shaka.net.NetworkingEngine.RequestType.SEGMENT) {
  280. this.onProgressUpdated_(response.timeMs, response.data.byteLength);
  281. }
  282. return response;
  283. }, (e) => {
  284. // Any error thrown from elsewhere should be recategorized as CRITICAL
  285. // here. This is because by the time it gets here, we've exhausted
  286. // retries.
  287. if (e) {
  288. goog.asserts.assert(e instanceof shaka.util.Error, 'Wrong error type');
  289. e.severity = shaka.util.Error.Severity.CRITICAL;
  290. }
  291. throw e;
  292. });
  293. // Return the pending request, which carries the response operation, and the
  294. // number of bytes remaining to be downloaded, updated by the progress
  295. // events. Add the operation to the manager for later cleanup.
  296. const pendingRequest =
  297. new shaka.net.NetworkingEngine.PendingRequest(
  298. op.promise, () => op.abort(), numBytesRemainingObj);
  299. this.operationManager_.manage(pendingRequest);
  300. return pendingRequest;
  301. }
  302. /**
  303. * @param {shaka.net.NetworkingEngine.RequestType} type
  304. * @param {shaka.extern.Request} request
  305. * @return {!shaka.util.AbortableOperation.<undefined>}
  306. * @private
  307. */
  308. filterRequest_(type, request) {
  309. let filterOperation = shaka.util.AbortableOperation.completed(undefined);
  310. for (const requestFilter of this.requestFilters_) {
  311. // Request filters are run sequentially.
  312. filterOperation = filterOperation.chain(() => {
  313. if (request.body) {
  314. // TODO: For v4.0 we should remove this or change to always pass a
  315. // Uint8Array. To make it easier for apps to write filters, it may be
  316. // better to always pass a Uint8Array so they know what they are
  317. // getting; but we shouldn't use ArrayBuffer since that would require
  318. // copying buffers if this is a partial view.
  319. request.body = shaka.util.BufferUtils.toArrayBuffer(request.body);
  320. }
  321. return requestFilter(type, request);
  322. });
  323. }
  324. // Catch any errors thrown by request filters, and substitute
  325. // them with a Shaka-native error.
  326. return filterOperation.chain(undefined, (e) => {
  327. if (e instanceof shaka.util.Error &&
  328. e.code == shaka.util.Error.Code.OPERATION_ABORTED) {
  329. // Don't change anything if the operation was aborted.
  330. throw e;
  331. }
  332. throw new shaka.util.Error(
  333. shaka.util.Error.Severity.CRITICAL,
  334. shaka.util.Error.Category.NETWORK,
  335. shaka.util.Error.Code.REQUEST_FILTER_ERROR, e);
  336. });
  337. }
  338. /**
  339. * @param {shaka.net.NetworkingEngine.RequestType} type
  340. * @param {shaka.extern.Request} request
  341. * @param {shaka.net.NetworkingEngine.NumBytesRemainingClass}
  342. * numBytesRemainingObj
  343. * @return {!shaka.extern.IAbortableOperation.<
  344. * shaka.net.NetworkingEngine.ResponseAndGotProgress>}
  345. * @private
  346. */
  347. makeRequestWithRetry_(type, request, numBytesRemainingObj) {
  348. const backoff = new shaka.net.Backoff(
  349. request.retryParameters, /* autoReset= */ false);
  350. const index = 0;
  351. return this.send_(
  352. type, request, backoff, index, /* lastError= */ null,
  353. numBytesRemainingObj);
  354. }
  355. /**
  356. * Sends the given request to the correct plugin and retry using Backoff.
  357. *
  358. * @param {shaka.net.NetworkingEngine.RequestType} type
  359. * @param {shaka.extern.Request} request
  360. * @param {!shaka.net.Backoff} backoff
  361. * @param {number} index
  362. * @param {?shaka.util.Error} lastError
  363. * @param {shaka.net.NetworkingEngine.NumBytesRemainingClass}
  364. * numBytesRemainingObj
  365. * @return {!shaka.extern.IAbortableOperation.<
  366. * shaka.net.NetworkingEngine.ResponseAndGotProgress>}
  367. * @private
  368. */
  369. send_(type, request, backoff, index, lastError, numBytesRemainingObj) {
  370. if (this.forceHTTPS_) {
  371. request.uris[index] = request.uris[index].replace('http://', 'https://');
  372. }
  373. const uri = new goog.Uri(request.uris[index]);
  374. let scheme = uri.getScheme();
  375. // Whether it got a progress event.
  376. let gotProgress = false;
  377. if (!scheme) {
  378. // If there is no scheme, infer one from the location.
  379. scheme = shaka.net.NetworkingEngine.getLocationProtocol_();
  380. goog.asserts.assert(
  381. scheme[scheme.length - 1] == ':',
  382. 'location.protocol expected to end with a colon!');
  383. // Drop the colon.
  384. scheme = scheme.slice(0, -1);
  385. // Override the original URI to make the scheme explicit.
  386. uri.setScheme(scheme);
  387. request.uris[index] = uri.toString();
  388. }
  389. // Schemes are meant to be case-insensitive.
  390. // See https://github.com/shaka-project/shaka-player/issues/2173
  391. // and https://tools.ietf.org/html/rfc3986#section-3.1
  392. scheme = scheme.toLowerCase();
  393. const object = shaka.net.NetworkingEngine.schemes_[scheme];
  394. const plugin = object ? object.plugin : null;
  395. if (!plugin) {
  396. return shaka.util.AbortableOperation.failed(
  397. new shaka.util.Error(
  398. shaka.util.Error.Severity.CRITICAL,
  399. shaka.util.Error.Category.NETWORK,
  400. shaka.util.Error.Code.UNSUPPORTED_SCHEME,
  401. uri));
  402. }
  403. const progressSupport = object.progressSupport;
  404. // Every attempt must have an associated backoff.attempt() call so that the
  405. // accounting is correct.
  406. const backoffOperation =
  407. shaka.util.AbortableOperation.notAbortable(backoff.attempt());
  408. /** @type {?shaka.util.Timer} */
  409. let connectionTimer = null;
  410. /** @type {?shaka.util.Timer} */
  411. let stallTimer = null;
  412. let aborted = false;
  413. let headersReceivedCalled = false;
  414. let startTimeMs;
  415. const sendOperation = backoffOperation.chain(() => {
  416. if (this.destroyed_) {
  417. return shaka.util.AbortableOperation.aborted();
  418. }
  419. startTimeMs = Date.now();
  420. const segment = shaka.net.NetworkingEngine.RequestType.SEGMENT;
  421. const progressUpdated = (time, bytes, numBytesRemaining) => {
  422. if (connectionTimer) {
  423. connectionTimer.stop();
  424. }
  425. if (stallTimer) {
  426. stallTimer.tickAfter(stallTimeoutMs / 1000);
  427. }
  428. if (this.onProgressUpdated_ && type == segment) {
  429. this.onProgressUpdated_(time, bytes);
  430. gotProgress = true;
  431. numBytesRemainingObj.setBytes(numBytesRemaining);
  432. }
  433. };
  434. const headersReceived = (headers) => {
  435. if (this.onHeadersReceived_) {
  436. this.onHeadersReceived_(headers, request, type);
  437. }
  438. headersReceivedCalled = true;
  439. };
  440. const requestPlugin = plugin(
  441. request.uris[index], request, type, progressUpdated, headersReceived);
  442. if (!progressSupport) {
  443. return requestPlugin;
  444. }
  445. const connectionTimeoutMs = request.retryParameters.connectionTimeout;
  446. if (connectionTimeoutMs) {
  447. connectionTimer = new shaka.util.Timer(() => {
  448. aborted = true;
  449. requestPlugin.abort();
  450. });
  451. connectionTimer.tickAfter(connectionTimeoutMs / 1000);
  452. }
  453. const stallTimeoutMs = request.retryParameters.stallTimeout;
  454. if (stallTimeoutMs) {
  455. stallTimer = new shaka.util.Timer(() => {
  456. aborted = true;
  457. requestPlugin.abort();
  458. });
  459. }
  460. return requestPlugin;
  461. }).chain((response) => {
  462. if (connectionTimer) {
  463. connectionTimer.stop();
  464. }
  465. if (stallTimer) {
  466. stallTimer.stop();
  467. }
  468. if (response.timeMs == undefined) {
  469. response.timeMs = Date.now() - startTimeMs;
  470. }
  471. const responseAndGotProgress = {
  472. response: response,
  473. gotProgress: gotProgress,
  474. };
  475. if (!headersReceivedCalled) {
  476. // The plugin did not call headersReceived, perhaps because it is not
  477. // able to track that information. So, fire the event manually.
  478. if (this.onHeadersReceived_) {
  479. this.onHeadersReceived_(response.headers, request, type);
  480. }
  481. }
  482. return responseAndGotProgress;
  483. }, (error) => {
  484. if (connectionTimer) {
  485. connectionTimer.stop();
  486. }
  487. if (stallTimer) {
  488. stallTimer.stop();
  489. }
  490. if (this.onDownloadFailed_) {
  491. let shakaError = null;
  492. let httpResponseCode = 0;
  493. if (error instanceof shaka.util.Error) {
  494. shakaError = error;
  495. if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) {
  496. httpResponseCode = /** @type {number} */ (error.data[1]);
  497. }
  498. }
  499. this.onDownloadFailed_(request, shakaError, httpResponseCode, aborted);
  500. }
  501. if (this.destroyed_) {
  502. return shaka.util.AbortableOperation.aborted();
  503. }
  504. if (aborted) {
  505. // It is necessary to change the error code to the correct one because
  506. // otherwise the retry logic would not work.
  507. error = new shaka.util.Error(
  508. shaka.util.Error.Severity.RECOVERABLE,
  509. shaka.util.Error.Category.NETWORK,
  510. shaka.util.Error.Code.TIMEOUT,
  511. request.uris[index], type);
  512. }
  513. if (error instanceof shaka.util.Error) {
  514. if (error.code == shaka.util.Error.Code.OPERATION_ABORTED) {
  515. // Don't change anything if the operation was aborted.
  516. throw error;
  517. } else if (error.code == shaka.util.Error.Code.ATTEMPTS_EXHAUSTED) {
  518. goog.asserts.assert(lastError, 'Should have last error');
  519. throw lastError;
  520. }
  521. if (error.severity == shaka.util.Error.Severity.RECOVERABLE) {
  522. const data = (new Map()).set('error', error);
  523. const event = new shaka.util.FakeEvent('retry', data);
  524. this.dispatchEvent(event);
  525. // Move to the next URI.
  526. index = (index + 1) % request.uris.length;
  527. return this.send_(
  528. type, request, backoff, index, error, numBytesRemainingObj);
  529. }
  530. }
  531. // The error was not recoverable, so do not try again.
  532. throw error;
  533. });
  534. return sendOperation;
  535. }
  536. /**
  537. * @param {shaka.net.NetworkingEngine.RequestType} type
  538. * @param {shaka.net.NetworkingEngine.ResponseAndGotProgress}
  539. * responseAndGotProgress
  540. * @return {!shaka.extern.IAbortableOperation.<
  541. * shaka.net.NetworkingEngine.ResponseAndGotProgress>}
  542. * @private
  543. */
  544. filterResponse_(type, responseAndGotProgress) {
  545. let filterOperation = shaka.util.AbortableOperation.completed(undefined);
  546. for (const responseFilter of this.responseFilters_) {
  547. // Response filters are run sequentially.
  548. filterOperation = filterOperation.chain(() => {
  549. const resp = responseAndGotProgress.response;
  550. if (resp.data) {
  551. // TODO: See TODO in filterRequest_.
  552. resp.data = shaka.util.BufferUtils.toArrayBuffer(resp.data);
  553. }
  554. return responseFilter(type, resp);
  555. });
  556. }
  557. // If successful, return the filtered response with whether it got
  558. // progress.
  559. return filterOperation.chain(() => {
  560. return responseAndGotProgress;
  561. }, (e) => {
  562. // Catch any errors thrown by request filters, and substitute
  563. // them with a Shaka-native error.
  564. // The error is assumed to be critical if the original wasn't a Shaka
  565. // error.
  566. let severity = shaka.util.Error.Severity.CRITICAL;
  567. if (e instanceof shaka.util.Error) {
  568. if (e.code == shaka.util.Error.Code.OPERATION_ABORTED) {
  569. // Don't change anything if the operation was aborted.
  570. throw e;
  571. }
  572. severity = e.severity;
  573. }
  574. throw new shaka.util.Error(
  575. severity,
  576. shaka.util.Error.Category.NETWORK,
  577. shaka.util.Error.Code.RESPONSE_FILTER_ERROR, e);
  578. });
  579. }
  580. /**
  581. * This is here only for testability. We can't mock location in our tests on
  582. * all browsers, so instead we mock this.
  583. *
  584. * @return {string} The value of location.protocol.
  585. * @private
  586. */
  587. static getLocationProtocol_() {
  588. return location.protocol;
  589. }
  590. };
  591. /**
  592. * A wrapper class for the number of bytes remaining to be downloaded for the
  593. * request.
  594. * Instead of using PendingRequest directly, this class is needed to be sent to
  595. * plugin as a parameter, and a Promise is returned, before PendingRequest is
  596. * created.
  597. *
  598. * @export
  599. */
  600. shaka.net.NetworkingEngine.NumBytesRemainingClass = class {
  601. /**
  602. * Constructor
  603. */
  604. constructor() {
  605. /** @private {number} */
  606. this.bytesToLoad_ = 0;
  607. }
  608. /**
  609. * @param {number} bytesToLoad
  610. */
  611. setBytes(bytesToLoad) {
  612. this.bytesToLoad_ = bytesToLoad;
  613. }
  614. /**
  615. * @return {number}
  616. */
  617. getBytes() {
  618. return this.bytesToLoad_;
  619. }
  620. };
  621. /**
  622. * A pending network request. This can track the current progress of the
  623. * download, and allows the request to be aborted if the network is slow.
  624. *
  625. * @implements {shaka.extern.IAbortableOperation.<shaka.extern.Response>}
  626. * @extends {shaka.util.AbortableOperation}
  627. * @export
  628. */
  629. shaka.net.NetworkingEngine.PendingRequest =
  630. class extends shaka.util.AbortableOperation {
  631. /**
  632. * @param {!Promise} promise
  633. * A Promise which represents the underlying operation. It is resolved
  634. * when the operation is complete, and rejected if the operation fails or
  635. * is aborted. Aborted operations should be rejected with a
  636. * shaka.util.Error object using the error code OPERATION_ABORTED.
  637. * @param {function():!Promise} onAbort
  638. * Will be called by this object to abort the underlying operation. This
  639. * is not cancelation, and will not necessarily result in any work being
  640. * undone. abort() should return a Promise which is resolved when the
  641. * underlying operation has been aborted. The returned Promise should
  642. * never be rejected.
  643. * @param {shaka.net.NetworkingEngine.NumBytesRemainingClass}
  644. * numBytesRemainingObj
  645. */
  646. constructor(promise, onAbort, numBytesRemainingObj) {
  647. super(promise, onAbort);
  648. /** @private {shaka.net.NetworkingEngine.NumBytesRemainingClass} */
  649. this.bytesRemaining_ = numBytesRemainingObj;
  650. }
  651. /**
  652. * @return {number}
  653. */
  654. getBytesRemaining() {
  655. return this.bytesRemaining_.getBytes();
  656. }
  657. };
  658. /**
  659. * Request types. Allows a filter to decide which requests to read/alter.
  660. *
  661. * @enum {number}
  662. * @export
  663. */
  664. shaka.net.NetworkingEngine.RequestType = {
  665. 'MANIFEST': 0,
  666. 'SEGMENT': 1,
  667. 'LICENSE': 2,
  668. 'APP': 3,
  669. 'TIMING': 4,
  670. 'SERVER_CERTIFICATE': 5,
  671. };
  672. /**
  673. * Priority level for network scheme plugins.
  674. * If multiple plugins are provided for the same scheme, only the
  675. * highest-priority one is used.
  676. *
  677. * @enum {number}
  678. * @export
  679. */
  680. shaka.net.NetworkingEngine.PluginPriority = {
  681. 'FALLBACK': 1,
  682. 'PREFERRED': 2,
  683. 'APPLICATION': 3,
  684. };
  685. /**
  686. * @typedef {{
  687. * plugin: shaka.extern.SchemePlugin,
  688. * priority: number,
  689. * progressSupport: boolean
  690. * }}
  691. * @property {shaka.extern.SchemePlugin} plugin
  692. * The associated plugin.
  693. * @property {number} priority
  694. * The plugin's priority.
  695. * @property {boolean} progressSupport
  696. * The plugin's supports progress events
  697. */
  698. shaka.net.NetworkingEngine.SchemeObject;
  699. /**
  700. * Contains the scheme plugins.
  701. *
  702. * @private {!Object.<string, shaka.net.NetworkingEngine.SchemeObject>}
  703. */
  704. shaka.net.NetworkingEngine.schemes_ = {};
  705. /**
  706. * @typedef {{
  707. * response: shaka.extern.Response,
  708. * gotProgress: boolean
  709. * }}
  710. *
  711. * @description
  712. * Defines a response wrapper object, including the response object and whether
  713. * progress event is fired by the scheme plugin.
  714. *
  715. * @property {shaka.extern.Response} response
  716. * @property {boolean} gotProgress
  717. * @private
  718. */
  719. shaka.net.NetworkingEngine.ResponseAndGotProgress;
  720. /**
  721. * @typedef {function(
  722. * !Object.<string, string>,
  723. * !shaka.extern.Request,
  724. * !shaka.net.NetworkingEngine.RequestType)}
  725. *
  726. * @description
  727. * A callback function that passes the shaka.extern.HeadersReceived along to
  728. * the player, plus some extra data.
  729. * @export
  730. */
  731. shaka.net.NetworkingEngine.OnHeadersReceived;
  732. /**
  733. * @typedef {function(
  734. * !shaka.extern.Request,
  735. * ?shaka.util.Error,
  736. * number,
  737. * boolean)}
  738. *
  739. * @description
  740. * A callback function that notifies the player when a download fails, for any
  741. * reason (e.g. even if the download was aborted).
  742. * @export
  743. */
  744. shaka.net.NetworkingEngine.OnDownloadFailed;