Skip to content

Commit

Permalink
feat(middleware): add range middleware factory
Browse files Browse the repository at this point in the history
  • Loading branch information
TomokiMiyauci committed Mar 10, 2023
1 parent 3fbef2e commit 1926b02
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 0 deletions.
18 changes: 18 additions & 0 deletions _dev_deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export {
assert,
assertEquals,
assertThrows,
} from "https://deno.land/std@0.178.0/testing/asserts.ts";
export { describe, it } from "https://deno.land/std@0.178.0/testing/bdd.ts";
export {
assertSpyCall,
assertSpyCalls,
spy,
} from "https://deno.land/std@0.178.0/testing/mock.ts";
export { Status } from "https://deno.land/std@0.178.0/http/http_status.ts";
export { equalsResponse } from "https://deno.land/x/http_utils@1.0.0-beta.13/response.ts";
export {
ConditionalHeader,
RangeHeader,
RepresentationHeader,
} from "https://deno.land/x/http_utils@1.0.0-beta.13/header.ts";
57 changes: 57 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
// This module is browser compatible.

export { Status } from "https://deno.land/std@0.178.0/http/http_status.ts";
export {
isNull,
isNumber,
isString,
} from "https://deno.land/x/isx@1.0.0-beta.24/mod.ts";
export { type Middleware } from "https://deno.land/x/http_middleware@1.0.0-beta.1/mod.ts";
export {
ConditionalHeader,
RangeHeader,
RepresentationHeader,
} from "https://deno.land/x/http_utils@1.0.0-beta.12/header.ts";
export { Method } from "https://deno.land/x/http_utils@1.0.0-beta.12/method.ts";
export { isErr, unsafe } from "https://deno.land/x/result_js@1.0.0/mod.ts";
import {
RepresentationHeader,
} from "https://deno.land/x/http_utils@1.0.0-beta.12/header.ts";
export {
type IntRange,
isIntRange,
isOtherRange,
isSuffixRange,
type OtherRange,
parse,
type Range,
type RangeSpec,
type RangesSpecifier,
type SuffixRange,
} from "https://deno.land/x/range_parser@1.0.0/mod.ts";
export { concat } from "https://deno.land/std@0.178.0/bytes/concat.ts";
export { toHashString } from "https://deno.land/std@0.178.0/crypto/to_hash_string.ts";

/** Create headers with no representation header fields. */
export class NoContentHeaders extends Headers {
constructor(init?: HeadersInit | undefined) {
super(init);

const allHeaders = [
RepresentationHeader.ContentEncoding,
RepresentationHeader.ContentLanguage,
RepresentationHeader.ContentLength,
RepresentationHeader.ContentLocation,
RepresentationHeader.ContentType,
RepresentationHeader.ETag,
RepresentationHeader.LastModified,
];

allHeaders.forEach(this.delete.bind(this));
}
}

export function isNotEmpty<T>(input: readonly T[]): input is [T, ...T[]] {
return !!input.length;
}
20 changes: 20 additions & 0 deletions middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// deno-lint-ignore-file no-explicit-any

import { type Middleware } from "./deps.ts";
import { withContentRange } from "./transform.ts";
import { BytesRange } from "./ranges/bytes.ts";
import type { Range } from "./types.ts";

interface Options {
readonly ranges?: readonly Range<any>[];
}

export function range(options?: Options): Middleware {
const ranges = options?.ranges ?? [new BytesRange() as Range<any>];

return async (request, next) => {
const response = await next(request);

return withContentRange(request, response, { ranges });
};
}
81 changes: 81 additions & 0 deletions middleware_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { range } from "./middleware.ts";
import {
assert,
describe,
equalsResponse,
it,
RangeHeader,
RepresentationHeader,
Status,
} from "./_dev_deps.ts";

describe("range", () => {
it("should", async () => {
const middleware = range();
const rangeRequest = new Request("test:", {
headers: { range: "bytes=5-9" },
});
const response = await middleware(
rangeRequest,
() => new Response("abcdefghijklmnopqrstuvwxyz"),
);

assert(
await equalsResponse(
response,
new Response(`fghij`, {
status: Status.PartialContent,
headers: {
[RangeHeader.ContentRange]: `bytes 5-9/26`,
},
}),
true,
),
);
});

it("should", async () => {
const middleware = range();
const rangeRequest = new Request("test:", {
headers: { range: "bytes=5-9, 20-, -5" },
});
const response = await middleware(
rangeRequest,
() => new Response("abcdefghijklmnopqrstuvwxyz"),
);

const boundary = `32d10c7b8cf96570ca04ce37f2a19d84240d3a89`;

assert(
await equalsResponse(
response,
new Response(
`--${boundary}
Content-Type: text/plain;charset=UTF-8
Content-Range: 5-9/26
fghij
--${boundary}
Content-Type: text/plain;charset=UTF-8
Content-Range: 20-25/26
uvwxyz
--${boundary}
Content-Type: text/plain;charset=UTF-8
Content-Range: 21-25/26
vwxyz
--${boundary}--`,
{
status: Status.PartialContent,
headers: {
[RepresentationHeader.ContentType]:
`multipart/byteranges; boundary=${boundary}`,
},
},
),
true,
),
);
});
});

0 comments on commit 1926b02

Please sign in to comment.