diff --git a/server.js b/server.js index b3d05c5..11151fe 100644 --- a/server.js +++ b/server.js @@ -7,6 +7,11 @@ const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const { WebSocketServer } = require('ws'); +const { trace } = require('@opentelemetry/api'); + +function traceId() { + return trace.getActiveSpan()?.spanContext().traceId; +} const PORT = Number(process.env.PORT || 8080); const HOST = process.env.HOST || '0.0.0.0'; @@ -92,6 +97,20 @@ function serveStatic(req, res) { } const server = http.createServer((req, res) => { + const start = process.hrtime.bigint(); + const { method, url } = req; + res.on('close', () => { + const ms = Number(process.hrtime.bigint() - start) / 1e6; + // res.writableEnded false here means the client (or ingress) dropped the + // connection before we finished writing — the smoking gun for a 502 that + // originates on the proxy side rather than in this process. + if (!res.writableEnded) { + console.warn(`[http] ABORTED ${method} ${url} after ${ms.toFixed(1)}ms (status=${res.statusCode}) trace=${traceId()}`); + } else if (ms > 1000) { + console.warn(`[http] SLOW ${method} ${url} ${res.statusCode} ${ms.toFixed(1)}ms trace=${traceId()}`); + } + }); + if (req.url === '/healthz' || req.url === '/readyz' || req.url === '/health') { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('ok'); @@ -110,6 +129,20 @@ const server = http.createServer((req, res) => { serveStatic(req, res); }); +// Node's default keepAliveTimeout (5s) is shorter than ingress-nginx's default +// upstream keepalive idle timeout (60s). Under that mismatch, nginx can pick a +// pooled connection Node has *just* closed and forward a request onto it, +// which comes back to the browser as a 502 — invisible from inside this +// process because Node never saw the request. headersTimeout must stay above +// keepAliveTimeout (Node enforces this). +server.keepAliveTimeout = 65_000; +server.headersTimeout = 66_000; + +server.on('clientError', (err, socket) => { + console.warn(`[http] clientError: ${err.message} (code=${err.code})`); + if (socket.writable) socket.end('HTTP/1.1 400 Bad Request\r\n\r\n'); +}); + // --------------------------------------------------------------------------- // Signaling // client -> server: {type: offer|answer|candidate, to} {type: pos|state}