Skip to content

Commit 13eb7cd

Browse files
committed
feat(preconditions): add checking accept-ranges header before evaluate if-ranges header
1 parent 96a51e1 commit 13eb7cd

File tree

6 files changed

+59
-7
lines changed

6 files changed

+59
-7
lines changed

_tools/meta.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,6 @@ export const makeOptions = (version: string): BuildOptions => ({
8686
name: "@httpland/http-utils",
8787
version: "1.0.0-beta.14",
8888
},
89-
"https://deno.land/x/[email protected]/mod.ts": {
90-
name: "@httpland/etag-parser",
91-
version: "1.0.0",
92-
},
9389
"https://deno.land/x/[email protected]/mod.ts": {
9490
name: "@miyauci/result",
9591
version: "1.0.0",
@@ -98,5 +94,9 @@ export const makeOptions = (version: string): BuildOptions => ({
9894
name: "@httpland/range-request-middleware",
9995
version: "1.0.0",
10096
},
97+
"https://deno.land/x/[email protected]/mod.ts": {
98+
name: "@httpland/accept-ranges-parser",
99+
version: "1.0.0",
100+
},
101101
},
102102
});

deno.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

deps.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ export {
4747
type RangesSpecifier,
4848
} from "https://deno.land/x/[email protected]/mod.ts";
4949
export { default as parseHttpDate } from "https://esm.sh/[email protected]";
50+
export {
51+
parseAcceptRanges,
52+
type Token,
53+
} from "https://deno.land/x/[email protected]/mod.ts";
5054

5155
export function not<T extends readonly unknown[]>(
5256
fn: (...args: T) => boolean,

preconditions/if_range.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ConditionalHeader,
77
isErr,
88
isNull,
9+
isString,
910
Method,
1011
type Range,
1112
RangeHeader,
@@ -15,6 +16,11 @@ import {
1516
} from "../deps.ts";
1617
import { Precondition } from "../types.ts";
1718
import { ifRange } from "./utils.ts";
19+
import { hasToken } from "../utils.ts";
20+
21+
const enum RangeUnit {
22+
None = "none",
23+
}
1824

1925
export function evaluateIfRange(
2026
request: Request,
@@ -29,15 +35,17 @@ export function evaluateIfRange(
2935
!request.headers.has(RangeHeader.Range)
3036
) return;
3137

38+
const acceptRanges = response.headers.get(RangeHeader.AcceptRanges);
39+
3240
/** An origin server MUST ignore an If-Range header field received in a request for a target resource that does not support Range requests. */
33-
// TODO:(miyauci) Check accept-ranges is none or not.
41+
if (isString(acceptRanges) && hasToken(acceptRanges, RangeUnit.None)) return;
3442

35-
const v = {
43+
const headers = {
3644
etag: response.headers.get(RepresentationHeader.ETag),
3745
lastModified: response.headers.get(RepresentationHeader.LastModified),
3846
};
3947

40-
const result = unsafe(() => ifRange(ifRangeValue, v));
48+
const result = unsafe(() => ifRange(ifRangeValue, headers));
4149

4250
if (isErr(result)) return;
4351

preconditions/if_range_test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,25 @@ describe("evaluateIfRange", () => {
7272
});
7373
});
7474

75+
it("should return undefined if the response has accept-ranges and include none", () => {
76+
assertEquals(
77+
evaluateIfRange(
78+
new Request("test:", {
79+
headers: {
80+
[ConditionalHeader.IfRange]: `""`,
81+
[RangeHeader.Range]: "",
82+
},
83+
}),
84+
new Response(null, {
85+
headers: {
86+
[RangeHeader.AcceptRanges]: "unknown, none",
87+
},
88+
}),
89+
),
90+
undefined,
91+
);
92+
});
93+
7594
it("should return false if the etag does not match strong", () => {
7695
const table: [Request, Response][] = [
7796
[

utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import {
1212
isSuccessfulStatus,
1313
Method,
1414
not,
15+
parseAcceptRanges,
1516
RepresentationHeader,
1617
Status,
1718
SuccessfulStatus,
19+
type Token,
1820
} from "./deps.ts";
1921
import type { Precondition } from "./types.ts";
2022

@@ -111,3 +113,14 @@ export function withoutConditionHeaders(
111113

112114
return newHeaders;
113115
}
116+
117+
/** Whether the input has {@link Token} or not.
118+
* If the input is invalid [`Accept-Ranges`](https://www.rfc-editor.org/rfc/rfc9110.html#section-14.3-2) then `false`.
119+
*/
120+
export function hasToken(input: string, token: Token): boolean {
121+
try {
122+
return parseAcceptRanges(input).includes(token);
123+
} catch {
124+
return false;
125+
}
126+
}

0 commit comments

Comments
 (0)