Skip to content
File

Blob: src/worker/webdav/request.ts

typescript57 lines
1import { davError } from "@/worker/dav/http";
2import { parseIfHeader, positiveLockTokens, type ParsedIfHeader } from "@/worker/dav/if-header";
3 
4export interface FileDavWriteRequest {
5 ifMatch: string | null;
6 ifNoneMatch: string | null;
7 ifHeader: ParsedIfHeader;
8 lockTokens: string[];
9 nowMs: number;
10}
11 
12export function parseContentLength(request: Request): number | null {
13 const value = request.headers.get("content-length");
14 if (!value) return null;
15 const parsed = Number.parseInt(value, 10);
16 return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
17}
18 
19export function requestIfHeader(request: Request): { ok: true; header: ParsedIfHeader } | Response {
20 const parsed = parseIfHeader(request.headers.get("if"));
21 if (!parsed.ok) return davError(400, parsed.message);
22 return parsed;
23}
24 
25export function readFileDavWriteRequest(request: Request, nowMs = Date.now()): FileDavWriteRequest | Response {
26 const parsedIf = requestIfHeader(request);
27 if (parsedIf instanceof Response) return parsedIf;
28 return {
29 ifMatch: request.headers.get("if-match"),
30 ifNoneMatch: request.headers.get("if-none-match"),
31 ifHeader: parsedIf.header,
32 lockTokens: positiveLockTokens(parsedIf.header),
33 nowMs,
34 };
35}
36 
37export function validateDeleteDepth(value: string | null): Response | null {
38 // RFC 4918 9.6.1: DELETE on a collection is Depth: infinity; other values are invalid.
39 const normalized = value?.trim().toLowerCase() ?? null;
40 return normalized !== null && normalized !== "infinity" ? davError(400, "DELETE Depth must be infinity") : null;
41}
42 
43export function readCopyDepth(value: string | null): "0" | "infinity" | Response {
44 // RFC 4918 9.8.3: COPY Depth must be absent, "0", or "infinity".
45 const normalized = value?.trim().toLowerCase() ?? null;
46 if (normalized !== null && normalized !== "0" && normalized !== "infinity") {
47 return davError(400, "COPY Depth must be 0 or infinity");
48 }
49 return normalized === "0" ? "0" : "infinity";
50}
51 
52export function validateMoveDepth(value: string | null): Response | null {
53 // RFC 4918 9.9.3: MOVE on a collection is Depth: infinity; Depth: 0 is not permitted.
54 const normalized = value?.trim().toLowerCase() ?? null;
55 return normalized !== null && normalized !== "infinity" ? davError(400, "MOVE Depth must be infinity") : null;
56}