From a4c2c1e5c43fa6de8e20fbf8cb5f4f804876ad51 Mon Sep 17 00:00:00 2001 From: Aleksandr Borovskii Date: Thu, 29 Jan 2026 07:37:26 +0100 Subject: [PATCH 01/10] fix: increase max HTTP header size to 32kb bytes across all environments --- Dockerfile | 2 ++ package.json | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1486c3d5..04634f9b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,6 +27,8 @@ ENV NPM_CONFIG_PREFIX $HOME/.npm ENV NPM_CONFIG_UPDATE_NOTIFIER false # add local bin dir to path ENV PATH $PATH:$NPM_CONFIG_PREFIX/bin +# increase max HTTP header size for node runtime +ENV NODE_OPTIONS --max-http-header-size=32768 # make folder for npm package RUN set -eux; \ diff --git a/package.json b/package.json index a04fa385..8d6fcea5 100644 --- a/package.json +++ b/package.json @@ -155,12 +155,12 @@ "lint": "eslint -c .eslintrc.js src tests", "format": "prettier --check src tests", "test:unit": "cross-env NODE_ENV=test jest src", - "test:e2e": "cross-env NODE_ENV=test jest tests/e2e", - "test:smoke": "cross-env NODE_ENV=test jest tests/smoke", + "test:e2e": "cross-env NODE_ENV=test NODE_OPTIONS=--max-http-header-size=32768 jest tests/e2e", + "test:smoke": "cross-env NODE_ENV=test NODE_OPTIONS=--max-http-header-size=32768 jest tests/smoke", "build": "webpack --config webpack.config.js --mode=production", - "build:pkg": "pkg .", + "build:pkg": "pkg --options max-http-header-size=32768 .", "prepare": "is-ci || husky install", - "start": "node -r ts-node/register/transpile-only -r tsconfig-paths/register src/index.ts" + "start": "cross-env NODE_OPTIONS=--max-http-header-size=32768 node -r ts-node/register/transpile-only -r tsconfig-paths/register src/index.ts" }, "brightCli": { "distribution": "unknown" From b2fb8d1b18aa55006481e228470269a8edcf4ed9 Mon Sep 17 00:00:00 2001 From: Aleksandr Borovskii Date: Thu, 29 Jan 2026 18:53:54 +0100 Subject: [PATCH 02/10] fix: remove NODE_OPTIONS override for max HTTP header size --- src/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index add9404e..13a7cbd8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,4 @@ process.env.UV_THREADPOOL_SIZE = String(1024); -process.env.NODE_OPTIONS = `${ - process.env.NODE_OPTIONS ?? '' -} --max-http-header-size=40960`.trim(); import 'reflect-metadata'; import { PollingScanStatus, From 338a5a732b3db6f76bd6985d1e7156fc9c45b8c9 Mon Sep 17 00:00:00 2001 From: Aleksandr Borovskii Date: Fri, 30 Jan 2026 12:35:16 +0100 Subject: [PATCH 03/10] fix: remove NODE_OPTIONS override for max HTTP header size in test scripts --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8d6fcea5..f9d4fd9a 100644 --- a/package.json +++ b/package.json @@ -155,8 +155,8 @@ "lint": "eslint -c .eslintrc.js src tests", "format": "prettier --check src tests", "test:unit": "cross-env NODE_ENV=test jest src", - "test:e2e": "cross-env NODE_ENV=test NODE_OPTIONS=--max-http-header-size=32768 jest tests/e2e", - "test:smoke": "cross-env NODE_ENV=test NODE_OPTIONS=--max-http-header-size=32768 jest tests/smoke", + "test:e2e": "cross-env NODE_ENV=test jest tests/e2e", + "test:smoke": "cross-env NODE_ENV=test jest tests/smoke", "build": "webpack --config webpack.config.js --mode=production", "build:pkg": "pkg --options max-http-header-size=32768 .", "prepare": "is-ci || husky install", From 46c36a7a7c8af9ee142d01d411afc3b894ddc3d6 Mon Sep 17 00:00:00 2001 From: Aleksandr Borovskii Date: Sun, 1 Feb 2026 23:19:43 +0100 Subject: [PATCH 04/10] fix: remove NODE_OPTIONS override for max HTTP header size in test scripts --- npm_bin/bright-cli.js | 34 +++++++++++++++++++++++++++++++ package.json | 5 +++-- src/Config/SystemConfigManager.ts | 3 +++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100755 npm_bin/bright-cli.js diff --git a/npm_bin/bright-cli.js b/npm_bin/bright-cli.js new file mode 100755 index 00000000..4470a20a --- /dev/null +++ b/npm_bin/bright-cli.js @@ -0,0 +1,34 @@ +#!/usr/bin/env node +'use strict'; + +const path = require('path'); +const { spawn } = require('child_process'); + +const ENTRY = path.join(__dirname, '../dist/index.js'); +const FLAG = '--max-http-header-size=32768'; + +// Prevent infinite recursion when we re-exec +if (process.env.BRIGHT_CLI_NPM_WRAPPER !== '1') { + const args = [FLAG, ENTRY, ...process.argv.slice(2)]; + + const child = spawn(process.execPath, args, { + stdio: 'inherit', + env: { ...process.env, BRIGHT_CLI_NPM_WRAPPER: '1' } + }); + + child.on('exit', (code, signal) => { + if (signal) process.kill(process.pid, signal); + process.exit(code ?? 0); + }); + + child.on('error', (err) => { + // eslint-disable-next-line no-console + console.error(err); + process.exit(1); + }); + + return; +} + +// If we’re already re-exec’d, just run the real CLI entry +require(ENTRY); diff --git a/package.json b/package.json index f9d4fd9a..2237f696 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "author": "Artem Derevnjuk ", "bin": { - "bright-cli": "./dist/index.js" + "bright-cli": "./npm_bin/bright-cli.js" }, "bugs": { "url": "https://github.com/NeuraLegion/bright-cli/issues" @@ -101,7 +101,8 @@ "wiremock-captain": "^4.1.3" }, "files": [ - "dist" + "dist", + "npm_bin" ], "homepage": "https://github.com/NeuraLegion/bright-cli#readme", "keywords": [ diff --git a/src/Config/SystemConfigManager.ts b/src/Config/SystemConfigManager.ts index 89bd2fc0..a2945646 100644 --- a/src/Config/SystemConfigManager.ts +++ b/src/Config/SystemConfigManager.ts @@ -4,6 +4,7 @@ import { readFile, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; import { homedir } from 'node:os'; import { setTimeout } from 'node:timers/promises'; +import http from 'node:http'; export interface SystemConfig { sentryDsn?: string; @@ -29,6 +30,8 @@ export class SystemConfigManager { clarifyTimeoutError: true } }); + + logger.debug('boba: maxHeaderSize', http.maxHeaderSize); } public async read(): Promise { From 9b142861c851bef91e40d631da22e0e3b4073093 Mon Sep 17 00:00:00 2001 From: Aleksandr Borovskii Date: Sun, 1 Feb 2026 23:27:58 +0100 Subject: [PATCH 05/10] fix: simplify npm wrapper --- npm_bin/bright-cli.js | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/npm_bin/bright-cli.js b/npm_bin/bright-cli.js index 4470a20a..7a03a1d9 100755 --- a/npm_bin/bright-cli.js +++ b/npm_bin/bright-cli.js @@ -1,34 +1,27 @@ #!/usr/bin/env node 'use strict'; +// eslint-disable-next-line @typescript-eslint/no-var-requires const path = require('path'); +// eslint-disable-next-line @typescript-eslint/no-var-requires const { spawn } = require('child_process'); const ENTRY = path.join(__dirname, '../dist/index.js'); -const FLAG = '--max-http-header-size=32768'; +const FLAG = '--max-http-header-size=32769'; -// Prevent infinite recursion when we re-exec -if (process.env.BRIGHT_CLI_NPM_WRAPPER !== '1') { - const args = [FLAG, ENTRY, ...process.argv.slice(2)]; +const args = [FLAG, ENTRY, ...process.argv.slice(2)]; - const child = spawn(process.execPath, args, { - stdio: 'inherit', - env: { ...process.env, BRIGHT_CLI_NPM_WRAPPER: '1' } - }); +const child = spawn(process.execPath, args, { stdio: 'inherit' }); - child.on('exit', (code, signal) => { - if (signal) process.kill(process.pid, signal); - process.exit(code ?? 0); - }); +child.on('exit', (code, signal) => { + if (signal) process.kill(process.pid, signal); + process.exit(code ?? 0); +}); - child.on('error', (err) => { - // eslint-disable-next-line no-console - console.error(err); - process.exit(1); - }); +child.on('error', (err) => { + // eslint-disable-next-line no-console + console.error(err); + process.exit(1); +}); - return; -} - -// If we’re already re-exec’d, just run the real CLI entry -require(ENTRY); +return; From c1e0bb8d227e7a4b031a3b3f69c8b4c99dd398a8 Mon Sep 17 00:00:00 2001 From: Alexander Borovsky Date: Sun, 1 Feb 2026 23:44:43 +0100 Subject: [PATCH 06/10] Update npm_bin/bright-cli.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- npm_bin/bright-cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm_bin/bright-cli.js b/npm_bin/bright-cli.js index 7a03a1d9..cee55fa5 100755 --- a/npm_bin/bright-cli.js +++ b/npm_bin/bright-cli.js @@ -7,7 +7,7 @@ const path = require('path'); const { spawn } = require('child_process'); const ENTRY = path.join(__dirname, '../dist/index.js'); -const FLAG = '--max-http-header-size=32769'; +const FLAG = '--max-http-header-size=32768'; const args = [FLAG, ENTRY, ...process.argv.slice(2)]; From d036544da207d3aaab6aad39d650b3741f888220 Mon Sep 17 00:00:00 2001 From: Aleksandr Borovskii Date: Sun, 1 Feb 2026 23:45:23 +0100 Subject: [PATCH 07/10] fix: remove debug logging for HTTP max header size --- src/Config/SystemConfigManager.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Config/SystemConfigManager.ts b/src/Config/SystemConfigManager.ts index a2945646..89bd2fc0 100644 --- a/src/Config/SystemConfigManager.ts +++ b/src/Config/SystemConfigManager.ts @@ -4,7 +4,6 @@ import { readFile, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; import { homedir } from 'node:os'; import { setTimeout } from 'node:timers/promises'; -import http from 'node:http'; export interface SystemConfig { sentryDsn?: string; @@ -30,8 +29,6 @@ export class SystemConfigManager { clarifyTimeoutError: true } }); - - logger.debug('boba: maxHeaderSize', http.maxHeaderSize); } public async read(): Promise { From f160b63b1f7c6165d4468d305178a50a031aad2b Mon Sep 17 00:00:00 2001 From: Aleksandr Borovskii Date: Sun, 1 Feb 2026 23:46:14 +0100 Subject: [PATCH 08/10] fix: remove unnecessary return statement and trailing newline in npm wrapper --- npm_bin/bright-cli.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/npm_bin/bright-cli.js b/npm_bin/bright-cli.js index cee55fa5..ae28f4b1 100755 --- a/npm_bin/bright-cli.js +++ b/npm_bin/bright-cli.js @@ -23,5 +23,3 @@ child.on('error', (err) => { console.error(err); process.exit(1); }); - -return; From d54fa64e6f420e6c0010acbcb773dc3a75549433 Mon Sep 17 00:00:00 2001 From: Aleksandr Borovskii Date: Mon, 2 Feb 2026 13:48:47 +0100 Subject: [PATCH 09/10] fix: move max HTTP header size configuration from runtime to request options --- Dockerfile | 2 -- npm_bin/bright-cli.js | 25 ---------------------- package.json | 9 ++++---- src/RequestExecutor/HttpRequestExecutor.ts | 3 ++- src/RequestExecutor/Request.ts | 1 + src/RequestExecutor/WsRequestExecutor.ts | 3 ++- 6 files changed, 9 insertions(+), 34 deletions(-) delete mode 100755 npm_bin/bright-cli.js diff --git a/Dockerfile b/Dockerfile index 04634f9b..1486c3d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,8 +27,6 @@ ENV NPM_CONFIG_PREFIX $HOME/.npm ENV NPM_CONFIG_UPDATE_NOTIFIER false # add local bin dir to path ENV PATH $PATH:$NPM_CONFIG_PREFIX/bin -# increase max HTTP header size for node runtime -ENV NODE_OPTIONS --max-http-header-size=32768 # make folder for npm package RUN set -eux; \ diff --git a/npm_bin/bright-cli.js b/npm_bin/bright-cli.js deleted file mode 100755 index ae28f4b1..00000000 --- a/npm_bin/bright-cli.js +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const path = require('path'); -// eslint-disable-next-line @typescript-eslint/no-var-requires -const { spawn } = require('child_process'); - -const ENTRY = path.join(__dirname, '../dist/index.js'); -const FLAG = '--max-http-header-size=32768'; - -const args = [FLAG, ENTRY, ...process.argv.slice(2)]; - -const child = spawn(process.execPath, args, { stdio: 'inherit' }); - -child.on('exit', (code, signal) => { - if (signal) process.kill(process.pid, signal); - process.exit(code ?? 0); -}); - -child.on('error', (err) => { - // eslint-disable-next-line no-console - console.error(err); - process.exit(1); -}); diff --git a/package.json b/package.json index 2237f696..a04fa385 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "author": "Artem Derevnjuk ", "bin": { - "bright-cli": "./npm_bin/bright-cli.js" + "bright-cli": "./dist/index.js" }, "bugs": { "url": "https://github.com/NeuraLegion/bright-cli/issues" @@ -101,8 +101,7 @@ "wiremock-captain": "^4.1.3" }, "files": [ - "dist", - "npm_bin" + "dist" ], "homepage": "https://github.com/NeuraLegion/bright-cli#readme", "keywords": [ @@ -159,9 +158,9 @@ "test:e2e": "cross-env NODE_ENV=test jest tests/e2e", "test:smoke": "cross-env NODE_ENV=test jest tests/smoke", "build": "webpack --config webpack.config.js --mode=production", - "build:pkg": "pkg --options max-http-header-size=32768 .", + "build:pkg": "pkg .", "prepare": "is-ci || husky install", - "start": "cross-env NODE_OPTIONS=--max-http-header-size=32768 node -r ts-node/register/transpile-only -r tsconfig-paths/register src/index.ts" + "start": "node -r ts-node/register/transpile-only -r tsconfig-paths/register src/index.ts" }, "brightCli": { "distribution": "unknown" diff --git a/src/RequestExecutor/HttpRequestExecutor.ts b/src/RequestExecutor/HttpRequestExecutor.ts index 09e6abc0..1387a72b 100644 --- a/src/RequestExecutor/HttpRequestExecutor.ts +++ b/src/RequestExecutor/HttpRequestExecutor.ts @@ -218,7 +218,8 @@ export class HttpRequestExecutor implements RequestExecutor { pfx: request.pfx, passphrase: request.passphrase, method: request.method, - rejectUnauthorized: false + rejectUnauthorized: false, + maxHeaderSize: Request.MAX_HEADERS_SIZE }; } diff --git a/src/RequestExecutor/Request.ts b/src/RequestExecutor/Request.ts index 6bba1df6..7db22a23 100644 --- a/src/RequestExecutor/Request.ts +++ b/src/RequestExecutor/Request.ts @@ -29,6 +29,7 @@ export interface Cert { } export class Request { + public static readonly MAX_HEADERS_SIZE = 32768; public static readonly SINGLE_VALUE_HEADERS: ReadonlySet = new Set([ 'authorization', diff --git a/src/RequestExecutor/WsRequestExecutor.ts b/src/RequestExecutor/WsRequestExecutor.ts index 854a9903..68157acf 100644 --- a/src/RequestExecutor/WsRequestExecutor.ts +++ b/src/RequestExecutor/WsRequestExecutor.ts @@ -180,7 +180,8 @@ export class WsRequestExecutor implements RequestExecutor { headers: this.normalizeHeaders(request.headers), ca: request.ca, pfx: request.pfx, - passphrase: request.passphrase + passphrase: request.passphrase, + maxHeaderSize: Request.MAX_HEADERS_SIZE }); const res: IncomingMessage = await this.connect(client); From 4f1be8164b709ed721801515d6650b812c4b231e Mon Sep 17 00:00:00 2001 From: Aleksandr Borovskii Date: Mon, 2 Feb 2026 14:17:43 +0100 Subject: [PATCH 10/10] refactor: extract MAX_HEADERS_SIZE constant to RequestExecutorConstants class --- src/RequestExecutor/HttpRequestExecutor.ts | 3 ++- src/RequestExecutor/Request.ts | 1 - src/RequestExecutor/RequestExecutorConstants.ts | 3 +++ src/RequestExecutor/WsRequestExecutor.ts | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 src/RequestExecutor/RequestExecutorConstants.ts diff --git a/src/RequestExecutor/HttpRequestExecutor.ts b/src/RequestExecutor/HttpRequestExecutor.ts index 1387a72b..916c5094 100644 --- a/src/RequestExecutor/HttpRequestExecutor.ts +++ b/src/RequestExecutor/HttpRequestExecutor.ts @@ -8,6 +8,7 @@ import { RequestExecutorOptions } from './RequestExecutorOptions'; import { NormalizeZlibDeflateTransformStream } from '../Utils/NormalizeZlibDeflateTransformStream'; import { CertificatesCache } from './CertificatesCache'; import { CertificatesResolver } from './CertificatesResolver'; +import { RequestExecutorConstants } from './RequestExecutorConstants'; import { inject, injectable } from 'tsyringe'; import iconv from 'iconv-lite'; import { safeParse } from 'fast-content-type-parse'; @@ -219,7 +220,7 @@ export class HttpRequestExecutor implements RequestExecutor { passphrase: request.passphrase, method: request.method, rejectUnauthorized: false, - maxHeaderSize: Request.MAX_HEADERS_SIZE + maxHeaderSize: RequestExecutorConstants.MAX_HEADERS_SIZE }; } diff --git a/src/RequestExecutor/Request.ts b/src/RequestExecutor/Request.ts index 7db22a23..6bba1df6 100644 --- a/src/RequestExecutor/Request.ts +++ b/src/RequestExecutor/Request.ts @@ -29,7 +29,6 @@ export interface Cert { } export class Request { - public static readonly MAX_HEADERS_SIZE = 32768; public static readonly SINGLE_VALUE_HEADERS: ReadonlySet = new Set([ 'authorization', diff --git a/src/RequestExecutor/RequestExecutorConstants.ts b/src/RequestExecutor/RequestExecutorConstants.ts new file mode 100644 index 00000000..f420f382 --- /dev/null +++ b/src/RequestExecutor/RequestExecutorConstants.ts @@ -0,0 +1,3 @@ +export class RequestExecutorConstants { + public static readonly MAX_HEADERS_SIZE = 32768; +} diff --git a/src/RequestExecutor/WsRequestExecutor.ts b/src/RequestExecutor/WsRequestExecutor.ts index 68157acf..ab418a8a 100644 --- a/src/RequestExecutor/WsRequestExecutor.ts +++ b/src/RequestExecutor/WsRequestExecutor.ts @@ -6,6 +6,7 @@ import { Helpers, logger, ProxyFactory } from '../Utils'; import { RequestExecutorOptions } from './RequestExecutorOptions'; import { CertificatesCache } from './CertificatesCache'; import { CertificatesResolver } from './CertificatesResolver'; +import { RequestExecutorConstants } from './RequestExecutorConstants'; import { inject, injectable } from 'tsyringe'; import WebSocket from 'ws'; import { once } from 'node:events'; @@ -181,7 +182,7 @@ export class WsRequestExecutor implements RequestExecutor { ca: request.ca, pfx: request.pfx, passphrase: request.passphrase, - maxHeaderSize: Request.MAX_HEADERS_SIZE + maxHeaderSize: RequestExecutorConstants.MAX_HEADERS_SIZE }); const res: IncomingMessage = await this.connect(client);