// server/http/common.js — the helpers every HTTP route shares: URL split, the error page and JSON replies. // Moved from server/index.js. const escapeHtml = (s) => String(s).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]); function errorPage(status, title, detail = '') { return ` ${status} · 卫戍协议:盟约

${status}

${escapeHtml(title)}

${detail ? `

${escapeHtml(detail)}

` : ''}

返回首页 · Back to home

`; } export function sendError(req, res, status, title, detail) { if (res.headersSent) { res.destroy(); return; } const body = Buffer.from(errorPage(status, title, detail)); res.writeHead(status, { 'Content-Type': 'text/html; charset=utf-8', 'Content-Length': body.length, 'Cache-Control': 'no-store' }); res.end(req.method === 'HEAD' ? undefined : body); } export function sendJson(req, res, status, obj) { const body = Buffer.from(JSON.stringify(obj)); res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': body.length, 'Cache-Control': 'no-store' }); res.end(req.method === 'HEAD' ? undefined : body); } /** Split an absolute request URL into raw path + query (also accepts absolute-form URLs). */ export function splitUrl(url) { let u = url || '/'; if (/^[a-z][a-z0-9+.-]*:\/\//i.test(u)) { try { const parsed = new URL(u); u = parsed.pathname + parsed.search; } catch { return null; } } const q = u.indexOf('?'); const hashless = (s) => { const h = s.indexOf('#'); return h >= 0 ? s.slice(0, h) : s; }; return q >= 0 ? { rawPath: hashless(u.slice(0, q)), query: hashless(u.slice(q + 1)) } : { rawPath: hashless(u), query: '' }; }