-
Notifications
You must be signed in to change notification settings - Fork 0
/
middleware.ts
89 lines (77 loc) · 2.71 KB
/
middleware.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
// This module is browser compatible.
import {
ConditionalHeader,
isNotEmpty,
isNull,
Method,
type Middleware,
RangeHeader,
} from "./deps.ts";
import { UnitLike, withAcceptRanges, withContentRange } from "./transform.ts";
import { BytesRange } from "./ranges/bytes.ts";
import { RangeUnit } from "./utils.ts";
import type { Range } from "./types.ts";
/** Create range request middleware.
*
* @example
* ```ts
* import { rangeRequest } from "https://deno.land/x/range_request_middleware@$VERSION/middleware.ts";
* import {
* assert,
* assertEquals,
* assertThrows,
* } from "https://deno.land/std/testing/asserts.ts";
*
* const middleware = rangeRequest();
* const request = new Request("test:", {
* headers: { range: "bytes=5-9" },
* });
* const response = await middleware(
* request,
* () => new Response("abcdefghijklmnopqrstuvwxyz"),
* );
*
* assertEquals(response.status, 206);
* assertEquals(response.headers.get("content-range"), "bytes 5-9/26");
* assertEquals(response.headers.get("accept-ranges"), "bytes");
* assertEquals(await response.text(), "fghij");
* ```
*/
export function rangeRequest(
ranges?: Iterable<Range>,
): Middleware {
const $ranges = ranges ?? [new BytesRange()];
const units = Array.from($ranges).map((range) => range.rangeUnit);
const unitLike = isNotEmpty(units) ? units : RangeUnit.None;
const contentRangeMiddleware = contentRange($ranges);
const acceptRangesMiddleware = acceptRanges(unitLike);
return (request, next) => {
return contentRangeMiddleware(
request,
(request) => acceptRangesMiddleware(request, next),
);
};
}
export function contentRange(ranges?: Iterable<Range>): Middleware {
const $ranges = ranges ?? [new BytesRange()];
return async (request, next) => {
const rangeValue = request.headers.get(RangeHeader.Range);
// A server MUST ignore a Range header field received with a request method that is unrecognized or for which range handling is not defined. For this specification, GET is the only method for which range handling is defined.
// @see https://www.rfc-editor.org/rfc/rfc9110#section-14.2-4
if (
request.method !== Method.Get ||
isNull(rangeValue) ||
request.headers.has(ConditionalHeader.IfRange)
) return next(request);
const response = await next(request);
return withContentRange(response, { ranges: $ranges, rangeValue });
};
}
export function acceptRanges(unitLike?: UnitLike): Middleware {
const rangeUnit = unitLike ?? RangeUnit.Bytes;
return async (request, next) => {
const response = await next(request);
return withAcceptRanges(response, rangeUnit);
};
}