diff --git a/server/index.js b/server/index.js index 2cf383e..bf0e504 100644 --- a/server/index.js +++ b/server/index.js @@ -6,6 +6,17 @@ const http = require('http'); const { randomUUID } = require('crypto'); require('dotenv').config(); +// Prevent HTTP parser errors (e.g. from keep-alive connections to monitored +// services that send data after Connection: close) from crashing the process. +process.on('uncaughtException', (err) => { + if (err.code === 'HPE_INVALID_CONSTANT' || (err.message && err.message.includes('Parse Error'))) { + console.error('HTTP parse error (swallowed):', err.message); + return; + } + console.error('Uncaught exception:', err); + process.exit(1); +}); + const db = require('./database'); const scheduler = require('./scheduler'); const monitorService = require('./monitorService'); diff --git a/server/monitorService.js b/server/monitorService.js index e353c59..ec044a5 100644 --- a/server/monitorService.js +++ b/server/monitorService.js @@ -31,9 +31,14 @@ function parseHostPort(urlString, fallbackPort) { class MonitorService { constructor() { - // Keep-alive agents for pooled HTTP/S connections + // Keep-alive agents for pooled HTTP/S connections. + // Socket-level 'error' listener prevents parse errors (e.g. "Data after + // Connection: close") from bubbling up as uncaught exceptions when a + // monitored server sends a malformed response on a reused connection. this.httpAgent = new http.Agent({ keepAlive: true, maxSockets: 50 }); + this.httpAgent.on('error', () => {}); this.httpsAgent = new https.Agent({ keepAlive: true, maxSockets: 50 }); + this.httpsAgent.on('error', () => {}); // Circuit breaker for HTTP checks (5 failures before opening, 60s reset) this.httpCircuitBreaker = new CircuitBreaker(5, 60000, this.isTransientError);