createPartialResponse.mjs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /*
  2. Copyright 2018 Google LLC
  3. Use of this source code is governed by an MIT-style
  4. license that can be found in the LICENSE file or at
  5. https://opensource.org/licenses/MIT.
  6. */
  7. import {WorkboxError} from 'workbox-core/_private/WorkboxError.mjs';
  8. import {assert} from 'workbox-core/_private/assert.mjs';
  9. import {logger} from 'workbox-core/_private/logger.mjs';
  10. import {calculateEffectiveBoundaries} from
  11. './utils/calculateEffectiveBoundaries.mjs';
  12. import {parseRangeHeader} from './utils/parseRangeHeader.mjs';
  13. import './_version.mjs';
  14. /**
  15. * Given a `Request` and `Response` objects as input, this will return a
  16. * promise for a new `Response`.
  17. *
  18. * If the original `Response` already contains partial content (i.e. it has
  19. * a status of 206), then this assumes it already fulfills the `Range:`
  20. * requirements, and will return it as-is.
  21. *
  22. * @param {Request} request A request, which should contain a Range:
  23. * header.
  24. * @param {Response} originalResponse A response.
  25. * @return {Promise<Response>} Either a `206 Partial Content` response, with
  26. * the response body set to the slice of content specified by the request's
  27. * `Range:` header, or a `416 Range Not Satisfiable` response if the
  28. * conditions of the `Range:` header can't be met.
  29. *
  30. * @memberof workbox.rangeRequests
  31. */
  32. async function createPartialResponse(request, originalResponse) {
  33. try {
  34. if (process.env.NODE_ENV !== 'production') {
  35. assert.isInstance(request, Request, {
  36. moduleName: 'workbox-range-requests',
  37. funcName: 'createPartialResponse',
  38. paramName: 'request',
  39. });
  40. assert.isInstance(originalResponse, Response, {
  41. moduleName: 'workbox-range-requests',
  42. funcName: 'createPartialResponse',
  43. paramName: 'originalResponse',
  44. });
  45. }
  46. if (originalResponse.status === 206) {
  47. // If we already have a 206, then just pass it through as-is;
  48. // see https://github.com/GoogleChrome/workbox/issues/1720
  49. return originalResponse;
  50. }
  51. const rangeHeader = request.headers.get('range');
  52. if (!rangeHeader) {
  53. throw new WorkboxError('no-range-header');
  54. }
  55. const boundaries = parseRangeHeader(rangeHeader);
  56. const originalBlob = await originalResponse.blob();
  57. const effectiveBoundaries = calculateEffectiveBoundaries(
  58. originalBlob, boundaries.start, boundaries.end);
  59. const slicedBlob = originalBlob.slice(effectiveBoundaries.start,
  60. effectiveBoundaries.end);
  61. const slicedBlobSize = slicedBlob.size;
  62. const slicedResponse = new Response(slicedBlob, {
  63. // Status code 206 is for a Partial Content response.
  64. // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206
  65. status: 206,
  66. statusText: 'Partial Content',
  67. headers: originalResponse.headers,
  68. });
  69. slicedResponse.headers.set('Content-Length', slicedBlobSize);
  70. slicedResponse.headers.set('Content-Range',
  71. `bytes ${effectiveBoundaries.start}-${effectiveBoundaries.end - 1}/` +
  72. originalBlob.size);
  73. return slicedResponse;
  74. } catch (error) {
  75. if (process.env.NODE_ENV !== 'production') {
  76. logger.warn(`Unable to construct a partial response; returning a ` +
  77. `416 Range Not Satisfiable response instead.`);
  78. logger.groupCollapsed(`View details here.`);
  79. logger.log(error);
  80. logger.log(request);
  81. logger.log(originalResponse);
  82. logger.groupEnd();
  83. }
  84. return new Response('', {
  85. status: 416,
  86. statusText: 'Range Not Satisfiable',
  87. });
  88. }
  89. }
  90. export {createPartialResponse};