{"id":34687982,"url":"https://github.com/httpland/range-request-middleware","last_synced_at":"2026-05-22T23:02:23.307Z","repository":{"id":135093548,"uuid":"612106812","full_name":"httpland/range-request-middleware","owner":"httpland","description":"HTTP range request middleware","archived":false,"fork":false,"pushed_at":"2023-05-02T03:07:31.000Z","size":117,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-16T13:02:39.442Z","etag":null,"topics":["byteranges","bytes","content-range","http","middleware","multipart","partial-content","range","range-request"],"latest_commit_sha":null,"homepage":"https://deno.land/x/range_request_middleware","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/httpland.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-10T08:02:38.000Z","updated_at":"2024-09-07T20:45:57.000Z","dependencies_parsed_at":"2024-10-06T12:33:48.520Z","dependency_job_id":"d025bf23-5bbb-4451-a7ba-1bfd4c0da158","html_url":"https://github.com/httpland/range-request-middleware","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/httpland/range-request-middleware","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/httpland%2Frange-request-middleware","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/httpland%2Frange-request-middleware/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/httpland%2Frange-request-middleware/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/httpland%2Frange-request-middleware/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/httpland","download_url":"https://codeload.github.com/httpland/range-request-middleware/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/httpland%2Frange-request-middleware/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33376007,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-22T21:56:13.512Z","status":"ssl_error","status_checked_at":"2026-05-22T21:56:10.769Z","response_time":265,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["byteranges","bytes","content-range","http","middleware","multipart","partial-content","range","range-request"],"created_at":"2025-12-24T21:55:38.862Z","updated_at":"2026-05-22T23:02:23.302Z","avatar_url":"https://github.com/httpland.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# range-request-middleware\n\n[![deno land](http://img.shields.io/badge/available%20on-deno.land/x-lightgrey.svg?logo=deno)](https://deno.land/x/range_request_middleware)\n[![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/range_request_middleware/mod.ts)\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/httpland/range-request-middleware)](https://github.com/httpland/range-request-middleware/releases)\n[![codecov](https://codecov.io/github/httpland/range-request-middleware/branch/main/graph/badge.svg)](https://codecov.io/gh/httpland/range-request-middleware)\n[![GitHub](https://img.shields.io/github/license/httpland/range-request-middleware)](https://github.com/httpland/range-request-middleware/blob/main/LICENSE)\n\n[![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)\n[![NPM](https://nodei.co/npm/@httpland/range-request-middleware.png?mini=true)](https://nodei.co/npm/@httpland/range-request-middleware/)\n\nHTTP range request middleware.\n\nHandles range request and partial response.\n\nCompliant with\n[RFC 9110, 14. Range Requests](https://www.rfc-editor.org/rfc/rfc9110#section-14)\n\n## Usage\n\nUpon receipt of a range request, if the response [satisfies](#satisfiable) the\nrange requirement, [convert](#convert) it to a partial response.\n\n```ts\nimport { rangeRequest } from \"https://deno.land/x/range_request_middleware@$VERSION/middleware.ts\";\nimport {\n  assert,\n  assertEquals,\n  assertThrows,\n} from \"https://deno.land/std/testing/asserts.ts\";\n\nconst middleware = rangeRequest();\nconst request = new Request(\"test:\", {\n  headers: { range: \"bytes=5-9\" },\n});\nconst response = await middleware(\n  request,\n  () =\u003e new Response(\"abcdefghijklmnopqrstuvwxyz\"),\n);\n\nassertEquals(response.status, 206);\nassertEquals(response.headers.get(\"content-range\"), \"bytes 5-9/26\");\nassertEquals(response.headers.get(\"accept-ranges\"), \"bytes\");\nassertEquals(await response.text(), \"fghij\");\n```\n\nyield:\n\n```http\nHTTP/1.1 206\nContent-Range: bytes 5-9/26\nAccept-Ranges: bytes\n\nfghij\n```\n\n## Multi-range request\n\nFor multi-range request, response body will convert to a multipart content.\n\nIt compliant with\n[RFC 9110, 14.6. Media Type multipart/byteranges](https://www.rfc-editor.org/rfc/rfc9110.html#name-media-type-multipart-bytera).\n\n```ts\nimport { rangeRequest } from \"https://deno.land/x/range_request_middleware@$VERSION/middleware.ts\";\nimport {\n  assert,\n  assertEquals,\n  assertThrows,\n} from \"https://deno.land/std/testing/asserts.ts\";\n\nconst middleware = rangeRequest();\nconst request = new Request(\"test:\", {\n  headers: { range: \"bytes=5-9, 20-, -5\" },\n});\nconst response = await middleware(\n  request,\n  () =\u003e new Response(\"abcdefghijklmnopqrstuvwxyz\"),\n);\n\nassertEquals(response.status, 206);\nassertEquals(\n  response.headers.get(\n    \"content-type\",\n  ),\n  \"multipart/byteranges; boundary=\u003cboundary-delimiter\u003e\",\n);\nassertEquals(\n  await response.text(),\n  `--\u003cboundary-delimiter\u003e\nContent-Type: text/plain;charset=UTF-8\nContent-Range: 5-9/26\n\nfghij\n--\u003cboundary-delimiter\u003e\nContent-Type: text/plain;charset=UTF-8\nContent-Range: 20-25/26\n\nuvwxyz\n--\u003cboundary-delimiter\u003e\nContent-Type: text/plain;charset=UTF-8\nContent-Range: 21-25/26\n\nvwxyz\n--\u003cboundary-delimiter\u003e--`,\n);\n```\n\nyield:\n\n```http\nHTTP/1.1 206\nContent-Type: multipart/byteranges; boundary=BOUNDARY\nAccept-Ranges: bytes\n\n--BOUNDARY\nContent-Type: text/plain;charset=UTF-8\nContent-Range: 5-9/26\n\nfghij\n--BOUNDARY\nContent-Type: text/plain;charset=UTF-8\nContent-Range: 20-25/26\n\nuvwxyz\n--BOUNDARY\nContent-Type: text/plain;charset=UTF-8\nContent-Range: 21-25/26\n\nvwxyz\n--BOUNDARY--\n```\n\n## Conditions\n\nThere are several conditions that must be met in order for middleware to\nexecute.\n\nIf the following conditions are **not met**,\n[invalid](https://www.rfc-editor.org/rfc/rfc9110#section-14.2-6) and the\nresponse will not [convert](#convert).\n\n- Request method is `GET`.\n- Request includes `Range` header\n- Request does not include `If-Range` header\n- Request `Range` header is valid syntax\n- Request `Range` header is valid semantics\n- Response status code is `200`\n- Response does not include `Content-Range` header\n- Response does not include `Accept-Ranges` header or its value is not `none`\n- Response body is readable\n\nNote that if there is an `If-Range` header, do nothing.\n\n## Unsatisfiable\n\nIf [conditions](#conditions) is met and the following conditions are **not met**\n,[unsatisfiable](https://www.rfc-editor.org/rfc/rfc9110#section-14.1.1-12), and\nit is not possible to meet partial response.\n\n- If a valid\n  [ranges-specifier](https://www.rfc-editor.org/rfc/rfc9110#rule.ranges-specifier)\n  contains at least one satisfactory\n  [range-spec](https://www.rfc-editor.org/rfc/rfc9110#rule.ranges-specifier), as\n  defined in the indicated\n  [range-unit](https://www.rfc-editor.org/rfc/rfc9110#range.units)\n\nIn this case, the handler response will [convert](#convert) to\n[416(Range Not Satisfiable)](https://www.rfc-editor.org/rfc/rfc9110#status.416)\nresponse.\n\nA example of how unsatisfiable can happen:\n\nIf receive un unknown range unit.\n\n```ts\nimport {\n  type Handler,\n  rangeRequest,\n} from \"https://deno.land/x/range_request_middleware@$VERSION/mod.ts\";\nimport { assert, assertEquals } from \"https://deno.land/std/testing/asserts.ts\";\n\ndeclare const handler: Handler;\nconst middleware = rangeRequest();\nconst response = await middleware(\n  new Request(\"test:\", { headers: { range: \"\u003cunknown-unit\u003e=\u003cother-range\u003e\" } }),\n  handler,\n);\n\nassertEquals(response.status, 416);\nassert(response.headers.has(\"content-range\"));\n```\n\n## Satisfiable\n\nIf the [conditions](#conditions) and [unsatisfiable](#unsatisfiable) are met,\n[satisfiable](https://www.rfc-editor.org/rfc/rfc9110#satisfiable), and the\nresponse will [convert](#convert) to\n[206(Partial Content)](https://www.rfc-editor.org/rfc/rfc9110#section-15.3.7)\nresponse.\n\n## Convert\n\nConvert means a change without side effects.\n\nFor example, \"convert a response to the 206 response\" means to return a new\nresponse in which some or all of the following elements have been replaced from\nthe original response.\n\n- HTTP Content\n- HTTP Status code\n- HTTP Headers(shallow marge)\n\n## Range\n\n`Range` abstracts partial response.\n\nMiddleware factories can accept `Range` objects and implement own range request\nprotocols.\n\n`Range` is the following structure:\n\n| Name      | Type                                                                                      | Description                                 |\n| --------- | ----------------------------------------------------------------------------------------- | ------------------------------------------- |\n| rangeUnit | `string`                                                                                  | Corresponding range unit.                   |\n| respond   | `(response: Response, context: RangesSpecifier) =\u003e` `Response` \u0026#124; `Promise\u003cResponse\u003e` | Return response from range request context. |\n\nThe middleware supports the following range request protocols by default:\n\n- `bytes`([ByteRanges](#bytesrange))\n\n### BytesRange\n\n`bytes` range unit is used to express subranges of a representation data's octet\nsequence.\n\nByteRange supports single and multiple range requests.\n\nCompliant with\n[RFC 9110, 14.1.2. Byte Ranges](https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2).\n\n```ts\nimport {\n  BytesRange,\n  type IntRange,\n  type SuffixRange,\n} from \"https://deno.land/x/range_request_middleware@$VERSION/mod.ts\";\nimport { assertEquals } from \"https://deno.land/std/testing/asserts.ts\";\n\nconst bytesRange = new BytesRange();\nconst rangeUnit = \"bytes\";\ndeclare const initResponse: Response;\ndeclare const rangeSet: [IntRange, SuffixRange];\n\nconst response = await bytesRange.respond(initResponse, {\n  rangeUnit,\n  rangeSet,\n});\n\nassertEquals(bytesRange.rangeUnit, rangeUnit);\nassertEquals(response.status, 206);\nassertEquals(\n  response.headers.get(\"content-type\"),\n  \"multipart/byteranges; boundary=\u003cBOUNDARY\u003e\",\n);\n```\n\n## Effects\n\nMiddleware may make changes to the following HTTP messages:\n\n- HTTP Content\n- HTTP Headers\n  - Accept-Ranges\n  - Content-Range\n  - Content-Type\n- HTTP Status code\n  - 206(Partial Content)\n  - 416(Range Not Satisfiable)\n\n## License\n\nCopyright © 2023-present [httpland](https://github.com/httpland).\n\nReleased under the [MIT](./LICENSE) license\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhttpland%2Frange-request-middleware","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhttpland%2Frange-request-middleware","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhttpland%2Frange-request-middleware/lists"}