Skip to content

Commit

Permalink
fix(transform): fix to change 416 response condition
Browse files Browse the repository at this point in the history
  • Loading branch information
TomokiMiyauci committed Mar 9, 2023
1 parent 0f1700c commit 2509e90
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 36 deletions.
64 changes: 31 additions & 33 deletions transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
parse,
RangeHeader,
type RangeSpec,
RangesSpecifier,
RepresentationHeader,
Status,
unsafe,
Expand Down Expand Up @@ -53,6 +52,7 @@ export async function withContentRange(
request.method !== Method.Get ||
isNull(rangeValue) ||
request.headers.has(ConditionalHeader.IfRange) ||
!response.ok ||
response.headers.has(RangeHeader.ContentRange) ||
response.headers.get(RangeHeader.AcceptRanges) === Unit.None ||
response.bodyUsed ||
Expand All @@ -68,23 +68,41 @@ export async function withContentRange(
}

const parsedRange = rangeContainer.value;
const validityResult = checkValidity(parsedRange, context.ranges);
// An origin server MUST ignore a Range header field that contains a range unit it does not understand. A proxy MAY discard a Range header field that contains a range unit it does not understand.
// @see https://www.rfc-editor.org/rfc/rfc9110#section-14.2-5
if (!validityResult) return response;

const matchedRange = validityResult;
const maybeRange = Array.from(context.ranges).find(matchRange);
const body = await response.clone().arrayBuffer();

if (!maybeRange) {
// @see https://www.rfc-editor.org/rfc/rfc9110#section-14.2-13
return new RequestedRangeNotSatisfiableResponse({
rangeUnit: parsedRange.rangeUnit,
completeLength: body.byteLength,
}, { headers: response.headers });
}

const isValid = parsedRange
.rangeSet
.map(toSpecifier)
.every((v) => maybeRange.specifiers.includes(v));

if (!isValid) {
// @see https://www.rfc-editor.org/rfc/rfc9110#section-14.2-13
return new RequestedRangeNotSatisfiableResponse({
rangeUnit: parsedRange.rangeUnit,
completeLength: body.byteLength,
}, { headers: response.headers });
}

const matchedRange = maybeRange;
const satisfiableRangeSet = parsedRange.rangeSet.filter(isSatisfiable);

if (!satisfiableRangeSet.length) {
return new RequestedRangeNotSatisfiableResponse({
rangeUnit: validityResult.unit,
rangeUnit: matchedRange.unit,
completeLength: body.byteLength,
}, { headers: response.headers });
}

const partialContents = await validityResult.partial({
const partialContents = await matchedRange.partial({
rangeSet: parsedRange.rangeSet,
content: body,
contentType,
Expand All @@ -102,6 +120,10 @@ export async function withContentRange(
function isSatisfiable(rangeSpec: RangeSpec): boolean {
return matchedRange.isSatisfiable({ contents: body, rangeSpec });
}

function matchRange(range: Range): boolean {
return range.unit === parsedRange.rangeUnit;
}
}

function toSpecifier(
Expand All @@ -112,27 +134,3 @@ function toSpecifier(

return "suffix-range";
}

function checkValidity(
range: RangesSpecifier,
ranges: Iterable<Range>,
): false | Range {
const parsedRange = range;
const maybeRange = Array.from(ranges).find(matchRange);

// An origin server MUST ignore a Range header field that contains a range unit it does not understand. A proxy MAY discard a Range header field that contains a range unit it does not understand.
// @see https://www.rfc-editor.org/rfc/rfc9110#section-14.2-5
if (!maybeRange) return false;

const matchedRange = maybeRange;
const nodes = parsedRange.rangeSet.map(toSpecifier);
const isValid = nodes.every((v) => matchedRange.specifiers.includes(v));

if (!isValid) return false;

return maybeRange;

function matchRange(range: Range): boolean {
return range.unit === parsedRange.rangeUnit;
}
}
46 changes: 43 additions & 3 deletions transform_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ describe("withContentRange", () => {
assert(initResponse === response);
});

it("if the response is not ok", async () => {
const initResponse = new Response(null, {
status: Status.NotFound,
headers: { [RangeHeader.ContentRange]: "" },
});
const response = await withContentRange(
new Request("test:", {
headers: { [RangeHeader.Range]: "bytes=0-" },
}),
initResponse,
{ ranges: [] },
);

assert(initResponse === response);
});

it("if the Content-Range header exists in response", async () => {
const initResponse = new Response(null, {
headers: { [RangeHeader.ContentRange]: "" },
Expand Down Expand Up @@ -114,7 +130,15 @@ describe("withContentRange", () => {
{ ranges: [] },
);

assert(initResponse === response);
assert(
equalsResponse(
response,
new Response(null, {
status: Status.RequestedRangeNotSatisfiable,
headers: { [RangeHeader.ContentRange]: "bytes */0" },
}),
),
);
});

it("if the range unit does not match", async () => {
Expand All @@ -133,7 +157,15 @@ describe("withContentRange", () => {
},
);

assert(initResponse === response);
assert(
equalsResponse(
response,
new Response(null, {
status: Status.RequestedRangeNotSatisfiable,
headers: { [RangeHeader.ContentRange]: "bytes */0" },
}),
),
);
});

it("if the range specifier does not match", async () => {
Expand All @@ -152,7 +184,15 @@ describe("withContentRange", () => {
},
);

assert(initResponse === response);
assert(
equalsResponse(
response,
new Response(null, {
status: Status.RequestedRangeNotSatisfiable,
headers: { [RangeHeader.ContentRange]: "bytes */0" },
}),
),
);
});
});

Expand Down

0 comments on commit 2509e90

Please sign in to comment.