An open API service indexing awesome lists of open source software.

https://github.com/httpland/range-request-middleware

HTTP range request middleware
https://github.com/httpland/range-request-middleware

byteranges bytes content-range http middleware multipart partial-content range range-request

Last synced: 22 days ago
JSON representation

HTTP range request middleware

Awesome Lists containing this project

README

          

# range-request-middleware

[![deno land](http://img.shields.io/badge/available%20on-deno.land/x-lightgrey.svg?logo=deno)](https://deno.land/x/range_request_middleware)
[![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/range_request_middleware/mod.ts)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/httpland/range-request-middleware)](https://github.com/httpland/range-request-middleware/releases)
[![codecov](https://codecov.io/github/httpland/range-request-middleware/branch/main/graph/badge.svg)](https://codecov.io/gh/httpland/range-request-middleware)
[![GitHub](https://img.shields.io/github/license/httpland/range-request-middleware)](https://github.com/httpland/range-request-middleware/blob/main/LICENSE)

[![test](https://github.com/httpland/range-request-middleware/actions/workflows/test.yaml/badge.svg)](https://github.com/httpland/range-request-middleware/actions/workflows/test.yaml)
[![NPM](https://nodei.co/npm/@httpland/range-request-middleware.png?mini=true)](https://nodei.co/npm/@httpland/range-request-middleware/)

HTTP range request middleware.

Handles range request and partial response.

Compliant with
[RFC 9110, 14. Range Requests](https://www.rfc-editor.org/rfc/rfc9110#section-14)

## Usage

Upon receipt of a range request, if the response [satisfies](#satisfiable) the
range requirement, [convert](#convert) it to a partial response.

```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");
```

yield:

```http
HTTP/1.1 206
Content-Range: bytes 5-9/26
Accept-Ranges: bytes

fghij
```

## Multi-range request

For multi-range request, response body will convert to a multipart content.

It compliant with
[RFC 9110, 14.6. Media Type multipart/byteranges](https://www.rfc-editor.org/rfc/rfc9110.html#name-media-type-multipart-bytera).

```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, 20-, -5" },
});
const response = await middleware(
request,
() => new Response("abcdefghijklmnopqrstuvwxyz"),
);

assertEquals(response.status, 206);
assertEquals(
response.headers.get(
"content-type",
),
"multipart/byteranges; boundary=",
);
assertEquals(
await response.text(),
`--
Content-Type: text/plain;charset=UTF-8
Content-Range: 5-9/26

fghij
--
Content-Type: text/plain;charset=UTF-8
Content-Range: 20-25/26

uvwxyz
--
Content-Type: text/plain;charset=UTF-8
Content-Range: 21-25/26

vwxyz
----`,
);
```

yield:

```http
HTTP/1.1 206
Content-Type: multipart/byteranges; boundary=BOUNDARY
Accept-Ranges: bytes

--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--
```

## Conditions

There are several conditions that must be met in order for middleware to
execute.

If the following conditions are **not met**,
[invalid](https://www.rfc-editor.org/rfc/rfc9110#section-14.2-6) and the
response will not [convert](#convert).

- Request method is `GET`.
- Request includes `Range` header
- Request does not include `If-Range` header
- Request `Range` header is valid syntax
- Request `Range` header is valid semantics
- Response status code is `200`
- Response does not include `Content-Range` header
- Response does not include `Accept-Ranges` header or its value is not `none`
- Response body is readable

Note that if there is an `If-Range` header, do nothing.

## Unsatisfiable

If [conditions](#conditions) is met and the following conditions are **not met**
,[unsatisfiable](https://www.rfc-editor.org/rfc/rfc9110#section-14.1.1-12), and
it is not possible to meet partial response.

- If a valid
[ranges-specifier](https://www.rfc-editor.org/rfc/rfc9110#rule.ranges-specifier)
contains at least one satisfactory
[range-spec](https://www.rfc-editor.org/rfc/rfc9110#rule.ranges-specifier), as
defined in the indicated
[range-unit](https://www.rfc-editor.org/rfc/rfc9110#range.units)

In this case, the handler response will [convert](#convert) to
[416(Range Not Satisfiable)](https://www.rfc-editor.org/rfc/rfc9110#status.416)
response.

A example of how unsatisfiable can happen:

If receive un unknown range unit.

```ts
import {
type Handler,
rangeRequest,
} from "https://deno.land/x/range_request_middleware@$VERSION/mod.ts";
import { assert, assertEquals } from "https://deno.land/std/testing/asserts.ts";

declare const handler: Handler;
const middleware = rangeRequest();
const response = await middleware(
new Request("test:", { headers: { range: "=" } }),
handler,
);

assertEquals(response.status, 416);
assert(response.headers.has("content-range"));
```

## Satisfiable

If the [conditions](#conditions) and [unsatisfiable](#unsatisfiable) are met,
[satisfiable](https://www.rfc-editor.org/rfc/rfc9110#satisfiable), and the
response will [convert](#convert) to
[206(Partial Content)](https://www.rfc-editor.org/rfc/rfc9110#section-15.3.7)
response.

## Convert

Convert means a change without side effects.

For example, "convert a response to the 206 response" means to return a new
response in which some or all of the following elements have been replaced from
the original response.

- HTTP Content
- HTTP Status code
- HTTP Headers(shallow marge)

## Range

`Range` abstracts partial response.

Middleware factories can accept `Range` objects and implement own range request
protocols.

`Range` is the following structure:

| Name | Type | Description |
| --------- | ----------------------------------------------------------------------------------------- | ------------------------------------------- |
| rangeUnit | `string` | Corresponding range unit. |
| respond | `(response: Response, context: RangesSpecifier) =>` `Response` | `Promise` | Return response from range request context. |

The middleware supports the following range request protocols by default:

- `bytes`([ByteRanges](#bytesrange))

### BytesRange

`bytes` range unit is used to express subranges of a representation data's octet
sequence.

ByteRange supports single and multiple range requests.

Compliant with
[RFC 9110, 14.1.2. Byte Ranges](https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2).

```ts
import {
BytesRange,
type IntRange,
type SuffixRange,
} from "https://deno.land/x/range_request_middleware@$VERSION/mod.ts";
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

const bytesRange = new BytesRange();
const rangeUnit = "bytes";
declare const initResponse: Response;
declare const rangeSet: [IntRange, SuffixRange];

const response = await bytesRange.respond(initResponse, {
rangeUnit,
rangeSet,
});

assertEquals(bytesRange.rangeUnit, rangeUnit);
assertEquals(response.status, 206);
assertEquals(
response.headers.get("content-type"),
"multipart/byteranges; boundary=",
);
```

## Effects

Middleware may make changes to the following HTTP messages:

- HTTP Content
- HTTP Headers
- Accept-Ranges
- Content-Range
- Content-Type
- HTTP Status code
- 206(Partial Content)
- 416(Range Not Satisfiable)

## License

Copyright © 2023-present [httpland](https://github.com/httpland).

Released under the [MIT](./LICENSE) license