From b1ef254f78b69bdf20d64421cc77d7ff68fff643 Mon Sep 17 00:00:00 2001 From: DeathStalker471 Date: Fri, 24 Apr 2026 12:53:35 -0700 Subject: [PATCH] fix: disable HTTP keepAlive (Node 18 behavior) with a config toggle (#5519) * implement disable keepalive, handle request-proxy and config logic * Invert keep-alive boolean setting * fix: clean-up server.js diff * fix: boolean flag type * feat: disable keep-alive by default --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com> --- default/config.yaml | 3 +++ src/command-line.js | 8 ++++++++ src/request-proxy.js | 6 ++++-- src/server-main.js | 8 +++++++- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/default/config.yaml b/default/config.yaml index bfa79c343..91d6cd291 100644 --- a/default/config.yaml +++ b/default/config.yaml @@ -41,6 +41,9 @@ port: 8000 # Interval in seconds to write a heartbeat file. Set to 0 to disable. # This is used primarily for Docker healthchecks. heartbeatInterval: 0 +# Enable HTTP/HTTPS keep-alive globally. +# Disabling restores old Node 18 behavior, can help if ECONNRESET and other network errors occur. +enableKeepAlive: false # -- SSL options -- ssl: # Enable SSL/TLS encryption diff --git a/src/command-line.js b/src/command-line.js index 7dec213d0..a9ed0bf5a 100644 --- a/src/command-line.js +++ b/src/command-line.js @@ -31,6 +31,7 @@ import { initConfig } from './config-init.js'; * @property {string} keyPassphrase SSL private key passphrase * @property {boolean} whitelistMode If enable whitelist mode * @property {boolean} basicAuthMode If enable basic authentication + * @property {boolean} enableKeepAlive Enable HTTP/HTTPS keep-alive globally * @property {boolean} requestProxyEnabled If enable outgoing request proxy * @property {string} requestProxyUrl Request proxy URL * @property {string[]} requestProxyBypass Request proxy bypass list @@ -76,6 +77,7 @@ export class CommandLineParser { keyPassphrase: '', whitelistMode: true, basicAuthMode: false, + enableKeepAlive: false, requestProxyEnabled: false, requestProxyUrl: '', requestProxyBypass: [], @@ -217,6 +219,11 @@ export class CommandLineParser { default: null, describe: 'Enables basic authentication', }) + .option('enableKeepAlive', { + type: 'boolean', + default: null, + describe: 'Enable HTTP/HTTPS keep-alive globally', + }) .option('requestProxyEnabled', { type: 'boolean', default: null, @@ -313,6 +320,7 @@ export class CommandLineParser { keyPassphrase: cliArguments.keyPassphrase ?? getConfigValue('ssl.keyPassphrase', defaultConfig.keyPassphrase), whitelistMode: cliArguments.whitelist ?? getConfigValue('whitelistMode', defaultConfig.whitelistMode, 'boolean'), basicAuthMode: cliArguments.basicAuthMode ?? getConfigValue('basicAuthMode', defaultConfig.basicAuthMode, 'boolean'), + enableKeepAlive: cliArguments.enableKeepAlive ?? getConfigValue('enableKeepAlive', defaultConfig.enableKeepAlive, 'boolean'), requestProxyEnabled: cliArguments.requestProxyEnabled ?? getConfigValue('requestProxy.enabled', defaultConfig.requestProxyEnabled, 'boolean'), requestProxyUrl: cliArguments.requestProxyUrl ?? getConfigValue('requestProxy.url', defaultConfig.requestProxyUrl), requestProxyBypass: cliArguments.requestProxyBypass ?? getConfigValue('requestProxy.bypass', defaultConfig.requestProxyBypass), diff --git a/src/request-proxy.js b/src/request-proxy.js index 92be93e83..496f917c1 100644 --- a/src/request-proxy.js +++ b/src/request-proxy.js @@ -13,8 +13,9 @@ const LOG_HEADER = '[Request Proxy]'; * @property {boolean} enabled Whether proxy is enabled. * @property {string} url Proxy URL. * @property {string[]} bypass List of URLs to bypass proxy. + * @property {boolean} enableKeepAlive Enable HTTP/HTTPS keep-alive. */ -export default function initRequestProxy({ enabled, url, bypass }) { +export default function initRequestProxy({ enabled, url, bypass, enableKeepAlive }) { try { // No proxy is enabled, so return if (!enabled) { @@ -39,7 +40,8 @@ export default function initRequestProxy({ enabled, url, bypass }) { process.env.no_proxy = bypass.join(','); } - const proxyAgent = new ProxyAgent(); + const proxyAgentOptions = enableKeepAlive ? { keepAlive: true } : { keepAlive: false }; + const proxyAgent = new ProxyAgent(proxyAgentOptions); http.globalAgent = proxyAgent; https.globalAgent = proxyAgent; diff --git a/src/server-main.js b/src/server-main.js index 423baf412..b412a63cd 100644 --- a/src/server-main.js +++ b/src/server-main.js @@ -5,6 +5,8 @@ import util from 'node:util'; import net from 'node:net'; import dns from 'node:dns'; import process from 'node:process'; +import http from 'node:http'; +import https from 'node:https'; import cors from 'cors'; import { csrfSync } from 'csrf-sync'; @@ -93,6 +95,10 @@ if (!cliArgs.enableIPv6 && !cliArgs.enableIPv4) { process.exit(1); } +// Set keep-alive preference for all HTTP/HTTPS requests. +http.globalAgent = new http.Agent({ keepAlive: cliArgs.enableKeepAlive }); +https.globalAgent = new https.Agent({ keepAlive: cliArgs.enableKeepAlive }); + const app = express(); app.use(helmet({ contentSecurityPolicy: false, @@ -328,7 +334,7 @@ async function preSetupTasks() { }); // Add request proxy. - initRequestProxy({ enabled: cliArgs.requestProxyEnabled, url: cliArgs.requestProxyUrl, bypass: cliArgs.requestProxyBypass }); + initRequestProxy({ enabled: cliArgs.requestProxyEnabled, url: cliArgs.requestProxyUrl, bypass: cliArgs.requestProxyBypass, enableKeepAlive: cliArgs.enableKeepAlive }); // Wait for frontend libs to compile await webpackMiddleware.runWebpackCompiler({ pruneCache: true });