import type { CheckResult, ResolvedHttpTarget } from "./types"; import { checkHttpExpect } from "./expect/http"; import { errorFailure } from "./expect/failure"; import { isError } from "es-toolkit"; export async function runHttpCheck(target: ResolvedHttpTarget): Promise { const timestamp = new Date().toISOString(); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), target.timeoutMs); try { const start = performance.now(); const response = await fetch(target.http.url, { method: target.http.method, headers: target.http.headers, body: target.http.method !== "GET" && target.http.method !== "HEAD" ? target.http.body : undefined, signal: controller.signal, }); const durationMs = Math.round(performance.now() - start); const statusCode = response.status; const responseHeaders = Object.fromEntries(response.headers); const hasBodyRules = !!(target.expect?.body && target.expect.body.length > 0); const preBodyExpect = target.expect ? { status: target.expect.status, maxDurationMs: target.expect.maxDurationMs, headers: target.expect.headers } : undefined; const preBodyObs = { statusCode, headers: responseHeaders, body: null as string | null, durationMs }; const preBodyResult = checkHttpExpect(preBodyObs, preBodyExpect); if (!hasBodyRules || !preBodyResult.matched) { clearTimeout(timeoutId); return { targetName: target.name, timestamp, matched: preBodyResult.matched, durationMs, statusDetail: `HTTP ${statusCode}`, failure: preBodyResult.failure, }; } const bodyBuffer = await response.arrayBuffer(); clearTimeout(timeoutId); if (bodyBuffer.byteLength > target.http.maxBodyBytes) { return { targetName: target.name, timestamp, matched: false, durationMs, statusDetail: `HTTP ${statusCode}`, failure: errorFailure( "body", "body", `响应体大小 ${bodyBuffer.byteLength} 超过限制 ${target.http.maxBodyBytes}`, ), }; } const body = new TextDecoder().decode(bodyBuffer); const fullObs = { statusCode, headers: responseHeaders, body, durationMs }; const fullResult = checkHttpExpect(fullObs, target.expect); return { targetName: target.name, timestamp, matched: fullResult.matched, durationMs, statusDetail: `HTTP ${statusCode}`, failure: fullResult.failure, }; } catch (error) { clearTimeout(timeoutId); const isTimeout = error instanceof DOMException && error.name === "AbortError"; return { targetName: target.name, timestamp, matched: false, durationMs: null, statusDetail: null, failure: errorFailure( "status", "request", isTimeout ? `请求超时 (${target.timeoutMs}ms)` : isError(error) ? error.message : String(error), ), }; } }