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>

b1ef254f78b69bdf20d64421cc77d7ff68fff643

DeathStalker471 <thomasnealson85@gmail.com>

Signed
4 files changed, +22 -3Ignore whitespace
default/config.yaml+3 -0
@@ -41,6 +41,9 @@ port: 8000
4141# Interval in seconds to write a heartbeat file. Set to 0 to disable.
4242# This is used primarily for Docker healthchecks.
4343heartbeatInterval: 0
44+# Enable HTTP/HTTPS keep-alive globally.
45+# Disabling restores old Node 18 behavior, can help if ECONNRESET and other network errors occur.
46+enableKeepAlive: false
4447# -- SSL options --
4548ssl:
4649 # Enable SSL/TLS encryption
src/command-line.js+8 -0
@@ -31,6 +31,7 @@ import { initConfig } from './config-init.js';
3131 * @property {string} keyPassphrase SSL private key passphrase
3232 * @property {boolean} whitelistMode If enable whitelist mode
3333 * @property {boolean} basicAuthMode If enable basic authentication
34+ * @property {boolean} enableKeepAlive Enable HTTP/HTTPS keep-alive globally
3435 * @property {boolean} requestProxyEnabled If enable outgoing request proxy
3536 * @property {string} requestProxyUrl Request proxy URL
3637 * @property {string[]} requestProxyBypass Request proxy bypass list
@@ -76,6 +77,7 @@ export class CommandLineParser {
7677 keyPassphrase: '',
7778 whitelistMode: true,
7879 basicAuthMode: false,
80+ enableKeepAlive: false,
7981 requestProxyEnabled: false,
8082 requestProxyUrl: '',
8183 requestProxyBypass: [],
@@ -217,6 +219,11 @@ export class CommandLineParser {
217219 default: null,
218220 describe: 'Enables basic authentication',
219221 })
222+ .option('enableKeepAlive', {
223+ type: 'boolean',
224+ default: null,
225+ describe: 'Enable HTTP/HTTPS keep-alive globally',
226+ })
220227 .option('requestProxyEnabled', {
221228 type: 'boolean',
222229 default: null,
@@ -313,6 +320,7 @@ export class CommandLineParser {
313320 keyPassphrase: cliArguments.keyPassphrase ?? getConfigValue('ssl.keyPassphrase', defaultConfig.keyPassphrase),
314321 whitelistMode: cliArguments.whitelist ?? getConfigValue('whitelistMode', defaultConfig.whitelistMode, 'boolean'),
315322 basicAuthMode: cliArguments.basicAuthMode ?? getConfigValue('basicAuthMode', defaultConfig.basicAuthMode, 'boolean'),
323+ enableKeepAlive: cliArguments.enableKeepAlive ?? getConfigValue('enableKeepAlive', defaultConfig.enableKeepAlive, 'boolean'),
316324 requestProxyEnabled: cliArguments.requestProxyEnabled ?? getConfigValue('requestProxy.enabled', defaultConfig.requestProxyEnabled, 'boolean'),
317325 requestProxyUrl: cliArguments.requestProxyUrl ?? getConfigValue('requestProxy.url', defaultConfig.requestProxyUrl),
318326 requestProxyBypass: cliArguments.requestProxyBypass ?? getConfigValue('requestProxy.bypass', defaultConfig.requestProxyBypass),
src/request-proxy.js+4 -2
@@ -13,8 +13,9 @@ const LOG_HEADER = '[Request Proxy]';
1313 * @property {boolean} enabled Whether proxy is enabled.
1414 * @property {string} url Proxy URL.
1515 * @property {string[]} bypass List of URLs to bypass proxy.
16+ * @property {boolean} enableKeepAlive Enable HTTP/HTTPS keep-alive.
1617 */
1718export default function initRequestProxy({ enabled, url, bypass, enableKeepAlive }) {
1819 try {
1920 // No proxy is enabled, so return
2021 if (!enabled) {
@@ -39,7 +40,8 @@ export default function initRequestProxy({ enabled, url, bypass }) {
3940 process.env.no_proxy = bypass.join(',');
4041 }
4142
42- const proxyAgent = new ProxyAgent();
43+ const proxyAgentOptions = enableKeepAlive ? { keepAlive: true } : { keepAlive: false };
44+ const proxyAgent = new ProxyAgent(proxyAgentOptions);
4345 http.globalAgent = proxyAgent;
4446 https.globalAgent = proxyAgent;
4547
src/server-main.js+7 -1
@@ -5,6 +5,8 @@ import util from 'node:util';
55import net from 'node:net';
66import dns from 'node:dns';
77import process from 'node:process';
8+import http from 'node:http';
9+import https from 'node:https';
810
911import cors from 'cors';
1012import { csrfSync } from 'csrf-sync';
@@ -93,6 +95,10 @@ if (!cliArgs.enableIPv6 && !cliArgs.enableIPv4) {
9395 process.exit(1);
9496}
9597
98+// Set keep-alive preference for all HTTP/HTTPS requests.
99+http.globalAgent = new http.Agent({ keepAlive: cliArgs.enableKeepAlive });
100+https.globalAgent = new https.Agent({ keepAlive: cliArgs.enableKeepAlive });
101+
96102const app = express();
97103app.use(helmet({
98104 contentSecurityPolicy: false,
@@ -328,7 +334,7 @@ async function preSetupTasks() {
328334 });
329335
330336 // Add request proxy.
331337 initRequestProxy({ enabled: cliArgs.requestProxyEnabled, url: cliArgs.requestProxyUrl, bypass: cliArgs.requestProxyBypass, enableKeepAlive: cliArgs.enableKeepAlive });
332338
333339 // Wait for frontend libs to compile
334340 await webpackMiddleware.runWebpackCompiler({ pruneCache: true });