From the logic audit (HEAD 04f5f7e):
- P0 CORS: enableCors methods lacked PUT — browsers could not chunk-upload
at all (cross-origin preflight refused)
- P0 chunked: validateUpload enforced the 2 GiB whole-body cap on chunked
completes, so every >2 GiB chunked upload failed after full upload
- P1 presignPut: the SDK's flexible-checksum default added an
x-amz-checksum-crc32 header browsers cannot reproduce; S3Client now uses
requestChecksumCalculation WHEN_REQUIRED
- P1 refresh rotation race: concurrent refreshes both succeeded (consume
ignored rowCount) forking the token family; consume now returns whether
it won and the loser revokes the chain
- P2 logout: awaits the revocation and ignores missing/forged bearers
honestly
37 lines
1.9 KiB
TypeScript
37 lines
1.9 KiB
TypeScript
import { HttpException, HttpStatus } from "@nestjs/common";
|
||
|
||
/** Carries the plaintext size for the controller’s unsatisfied Content-Range header. */
|
||
export class FileRangeNotSatisfiableException extends HttpException {
|
||
constructor(readonly total: number) {
|
||
super("requested range is not satisfiable", HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
|
||
}
|
||
}
|
||
|
||
/** Inclusive HTTP byte bounds, clipped to the plaintext EOF. */
|
||
export function playbackRange(total: number, start: number, end: number): { start: number; end: number } {
|
||
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || start >= total || end < start) {
|
||
throw new FileRangeNotSatisfiableException(total);
|
||
}
|
||
return { start, end: Math.min(end, total - 1) };
|
||
}
|
||
|
||
/** Query bounds take precedence; also accept the single Range browsers send. */
|
||
export function requestedPlaybackRange(total: number, start: unknown, end: unknown, range?: string): { start: number; end: number } {
|
||
const fail = (): never => { throw new FileRangeNotSatisfiableException(total); };
|
||
const integer = (value: unknown): number => {
|
||
if (typeof value !== "string" || !/^\d+$/.test(value) || !Number.isSafeInteger(Number(value))) return fail();
|
||
return Number(value);
|
||
};
|
||
if (start !== undefined || end !== undefined) {
|
||
return playbackRange(total, start === undefined ? 0 : integer(start), end === undefined ? total - 1 : integer(end));
|
||
}
|
||
if (range === undefined) return playbackRange(total, 0, total - 1);
|
||
const match = /^bytes=(\d*)-(\d*)$/.exec(range);
|
||
if (match === null || (match[1] === "" && match[2] === "")) return fail();
|
||
if (match[1] === "") {
|
||
const suffix = integer(match[2]);
|
||
if (suffix === 0) return fail();
|
||
return playbackRange(total, Math.max(0, total - suffix), total - 1);
|
||
}
|
||
return playbackRange(total, integer(match[1]), match[2] === "" ? total - 1 : integer(match[2]));
|
||
}
|