import { IncomingMessage, ServerResponse } from 'http'; export interface RequestContext { method: string; url: URL; pathname: string; headers: IncomingMessage['headers']; remoteAddress: string; bearerToken: string | null; } const MAX_BODY_BYTES = 1 * 1024 * 1024; // 1 MiB export function getBearerToken(headers: IncomingMessage['headers']): string | null { const raw = headers.authorization; if (typeof raw !== 'string') { return null; } const trimmed = raw.trim(); if (!/^bearer\s+/iu.test(trimmed)) { return null; } const token = trimmed.replace(/^bearer\s+/iu, '').trim(); return token.length > 0 ? token : null; } export async function readJsonBody(req: IncomingMessage): Promise { const length = Number(req.headers['content-length'] ?? 0); if (length > MAX_BODY_BYTES) { throw new HttpError(413, 'Request body too large', 'BODY_TOO_LARGE'); } const chunks: Buffer[] = []; let received = 0; for await (const chunk of req) { const buffer = chunk instanceof Buffer ? chunk : Buffer.from(chunk as string); received += buffer.length; if (received > MAX_BODY_BYTES) { throw new HttpError(413, 'Request body too large', 'BODY_TOO_LARGE'); } chunks.push(buffer); } if (chunks.length === 0) { return {} as T; } const raw = Buffer.concat(chunks).toString('utf8'); try { return JSON.parse(raw) as T; } catch { throw new HttpError(400, 'Invalid JSON body', 'INVALID_JSON'); } } export function sendJson(res: ServerResponse, status: number, payload: unknown): void { if (!res.headersSent) { res.statusCode = status; res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.setHeader('Cache-Control', 'no-store'); } res.end(JSON.stringify(payload)); } export function sendText(res: ServerResponse, status: number, text: string, contentType = 'text/plain; charset=utf-8'): void { if (!res.headersSent) { res.statusCode = status; res.setHeader('Content-Type', contentType); res.setHeader('Cache-Control', 'no-store'); } res.end(text); } export class HttpError extends Error { readonly status: number; readonly code: string; constructor(status: number, message: string, code: string) { super(message); this.status = status; this.code = code; } } export function sendError(res: ServerResponse, error: unknown): void { if (error instanceof HttpError) { sendJson(res, error.status, { error: error.message, errorCode: error.code }); return; } const message = error instanceof Error ? error.message : 'Internal server error'; sendJson(res, 500, { error: message, errorCode: 'INTERNAL_ERROR' }); }