| ... | ... | @@ -161,7 +161,7 @@ app.get("/file/*", async (c, next) => { |
| 161 | 161 | // Range requests |
| 162 | 162 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests |
| 163 | 163 | if (rangeHeader) { |
| 164 | | const ranges = parseRange(rangeHeader, file.size); |
| 164 | const ranges = http.parseRangeHeader(rangeHeader, file.size); |
| 165 | 165 | if (ranges?.[0] && ranges.length === 1) { |
| 166 | 166 | start = ranges[0][0]; |
| 167 | 167 | end = ranges[0][1]; |
| ... | ... | @@ -216,39 +216,6 @@ function ifRangeOutdated(file: MediaFile, ifRangeHeader: string) { |
| 216 | 216 | return new Date(ifRangeHeader) < file.date; |
| 217 | 217 | } |
| 218 | 218 | |
| 219 | | /** The end is inclusive */ |
| 220 | | type Ranges = Array<[start: number, end: number]>; |
| 221 | | |
| 222 | | function parseRange(rangeHeader: string, fileSize: number): Ranges | null { |
| 223 | | const [unit, ranges] = rangeHeader.split("="); |
| 224 | | if (!ranges) return null; |
| 225 | | if (unit !== "bytes") return null; |
| 226 | | |
| 227 | | const result: Array<[start: number, end: number]> = []; |
| 228 | | const rangeParts = ranges.split(","); |
| 229 | | |
| 230 | | for (const range of rangeParts) { |
| 231 | | const split = range.split("-"); |
| 232 | | const [start, end] = split; |
| 233 | | if (!start || !end) return null; |
| 234 | | if (start === "" && end === "") return null; |
| 235 | | const parsedRange: [number, number] = [ |
| 236 | | start === "" ? fileSize - +end : +start, |
| 237 | | end === "" ? fileSize - 1 : +end, |
| 238 | | ]; |
| 239 | | result.push(parsedRange); |
| 240 | | } |
| 241 | | |
| 242 | | // Validate that ranges do not intersect |
| 243 | | result.sort((a, b) => a[0] - b[0]); |
| 244 | | for (let i = 1; i < result.length; i++) { |
| 245 | | if (UNWRAP(result[i])[0] <= UNWRAP(result[i - 1])[1]) { |
| 246 | | return null; |
| 247 | | } |
| 248 | | } |
| 249 | | |
| 250 | | return result; |
| 251 | | } |
| 252 | 219 | |
| 253 | 220 | function checkCotyledonCookie(c: Context) { |
| 254 | 221 | const cookie = c.req.header("Cookie"); |
| ... | ... | @@ -317,6 +284,7 @@ import { type Context, Hono } from "hono"; |
| 317 | 284 | import * as render from "@clo/lib/render"; |
| 318 | 285 | import { etagMatches, serveAsset } from "#sitegen/assets"; |
| 319 | 286 | import * as view from "#sitegen/view"; |
| 287 | import * as http from "@clo/lib/http"; |
| 320 | 288 | import * as mime from "@clo/lib/mime"; |
| 321 | 289 | |
| 322 | 290 | import { getForFile, requireFriendAuth } from "#src/friend-auth.ts"; |