createPartialResponse.js 3.74 KB
/*
  Copyright 2018 Google LLC

  Use of this source code is governed by an MIT-style
  license that can be found in the LICENSE file or at
  https://opensource.org/licenses/MIT.
*/
import { WorkboxError } from 'workbox-core/_private/WorkboxError.js';
import { assert } from 'workbox-core/_private/assert.js';
import { logger } from 'workbox-core/_private/logger.js';
import { calculateEffectiveBoundaries } from './utils/calculateEffectiveBoundaries.js';
import { parseRangeHeader } from './utils/parseRangeHeader.js';
import './_version.js';
/**
 * Given a `Request` and `Response` objects as input, this will return a
 * promise for a new `Response`.
 *
 * If the original `Response` already contains partial content (i.e. it has
 * a status of 206), then this assumes it already fulfills the `Range:`
 * requirements, and will return it as-is.
 *
 * @param {Request} request A request, which should contain a Range:
 * header.
 * @param {Response} originalResponse A response.
 * @return {Promise<Response>} Either a `206 Partial Content` response, with
 * the response body set to the slice of content specified by the request's
 * `Range:` header, or a `416 Range Not Satisfiable` response if the
 * conditions of the `Range:` header can't be met.
 *
 * @memberof module:workbox-range-requests
 */
async function createPartialResponse(request, originalResponse) {
    try {
        if (process.env.NODE_ENV !== 'production') {
            assert.isInstance(request, Request, {
                moduleName: 'workbox-range-requests',
                funcName: 'createPartialResponse',
                paramName: 'request',
            });
            assert.isInstance(originalResponse, Response, {
                moduleName: 'workbox-range-requests',
                funcName: 'createPartialResponse',
                paramName: 'originalResponse',
            });
        }
        if (originalResponse.status === 206) {
            // If we already have a 206, then just pass it through as-is;
            // see https://github.com/GoogleChrome/workbox/issues/1720
            return originalResponse;
        }
        const rangeHeader = request.headers.get('range');
        if (!rangeHeader) {
            throw new WorkboxError('no-range-header');
        }
        const boundaries = parseRangeHeader(rangeHeader);
        const originalBlob = await originalResponse.blob();
        const effectiveBoundaries = calculateEffectiveBoundaries(originalBlob, boundaries.start, boundaries.end);
        const slicedBlob = originalBlob.slice(effectiveBoundaries.start, effectiveBoundaries.end);
        const slicedBlobSize = slicedBlob.size;
        const slicedResponse = new Response(slicedBlob, {
            // Status code 206 is for a Partial Content response.
            // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206
            status: 206,
            statusText: 'Partial Content',
            headers: originalResponse.headers,
        });
        slicedResponse.headers.set('Content-Length', String(slicedBlobSize));
        slicedResponse.headers.set('Content-Range', `bytes ${effectiveBoundaries.start}-${effectiveBoundaries.end - 1}/` +
            originalBlob.size);
        return slicedResponse;
    }
    catch (error) {
        if (process.env.NODE_ENV !== 'production') {
            logger.warn(`Unable to construct a partial response; returning a ` +
                `416 Range Not Satisfiable response instead.`);
            logger.groupCollapsed(`View details here.`);
            logger.log(error);
            logger.log(request);
            logger.log(originalResponse);
            logger.groupEnd();
        }
        return new Response('', {
            status: 416,
            statusText: 'Range Not Satisfiable',
        });
    }
}
export { createPartialResponse };