UNPKG

@httpland/range-request-middleware

Version:
40 lines (39 loc) 2.21 kB
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license. // This module is browser compatible. import { distinct, isErr, isString, parseRange, RangeHeader, Status, unsafe, } from "./deps.js"; import { equalsCaseInsensitive, hasToken, RangeUnit as Unit, RequestedRangeNotSatisfiableResponse, } from "./utils.js"; export function withAcceptRanges(response, unit) { const units = isString(unit) ? [unit] : unit; const unitValue = distinct(units).join(", "); if (!response.headers.has(RangeHeader.AcceptRanges)) { response.headers.set(RangeHeader.AcceptRanges, unitValue); } return response; } export async function withContentRange(response, context) { if (response.status !== Status.OK || response.headers.has(RangeHeader.ContentRange) || response.bodyUsed) return response; const acceptRanges = response.headers.get(RangeHeader.AcceptRanges); if (isString(acceptRanges) && hasToken(acceptRanges, Unit.None)) { return response; } const rangeContainer = unsafe(() => parseRange(context.rangeValue)); if (isErr(rangeContainer)) { // A server that supports range requests MAY ignore or reject a Range header field that contains an invalid ranges-specifier (Section 14.1.1), a ranges-specifier with more than two overlapping ranges, or a set of many small ranges that are not listed in ascending order, since these are indications of either a broken client or a deliberate denial-of-service attack (Section 17.15). // https://www.rfc-editor.org/rfc/rfc9110#section-14.2-6 return response; } const parsedRange = rangeContainer.value; const matchedRange = Array.from(context.ranges).find(({ rangeUnit }) => equalsCaseInsensitive(rangeUnit, parsedRange.rangeUnit)); const body = await response.clone().arrayBuffer(); if (!matchedRange) { // @see https://www.rfc-editor.org/rfc/rfc9110#section-14.2-13 return new RequestedRangeNotSatisfiableResponse({ rangeUnit: parsedRange.rangeUnit, completeLength: body.byteLength, }, { headers: response.headers }); } return matchedRange.respond(response, parsedRange); }