import type { IncomingMessage } from "node:http"; export async function readBody(req: IncomingMessage): Promise { const chunks: Buffer[] = []; for await (const c of req) chunks.push(c as Buffer); return Buffer.concat(chunks); } export interface MultipartPart { name: string; filename?: string; data: Buffer; } /** 极简 multipart/form-data 解析,仅供 mock 使用。 */ export function parseMultipart(body: Buffer, contentType: string): MultipartPart[] { const m = /boundary=(?:"([^"]+)"|([^;]+))/i.exec(contentType); if (!m) return []; const boundary = Buffer.from(`--${m[1] ?? m[2]}`); const parts: MultipartPart[] = []; let pos = body.indexOf(boundary); while (pos >= 0) { const start = pos + boundary.length; if (body.subarray(start, start + 2).toString() === "--") break; const next = body.indexOf(boundary, start); if (next < 0) break; const chunk = body.subarray(start + 2, next - 2); // 去掉起始 CRLF 与结尾 CRLF const sep = chunk.indexOf("\r\n\r\n"); const head = chunk.subarray(0, sep).toString(); const data = chunk.subarray(sep + 4); const name = /name="([^"]*)"/.exec(head)?.[1] ?? ""; const filename = /filename="([^"]*)"/.exec(head)?.[1]; parts.push({ name, ...(filename !== undefined ? { filename } : {}), data }); pos = next; } return parts; } export function parseCookies(header: string | undefined): Record { const out: Record = {}; for (const p of (header ?? "").split(";")) { const i = p.indexOf("="); if (i > 0) out[p.slice(0, i).trim()] = p.slice(i + 1).trim(); } return out; }