From ec3c1cc14a3af410226956b72ad8743ce59b750b Mon Sep 17 00:00:00 2001 From: Yurii Bliuchak <1957659+bliuchak@users.noreply.github.com> Date: Sun, 11 Jan 2026 17:22:23 +0100 Subject: [PATCH 1/2] fix: correct localhost-test resolving on linux machines --- package.json | 2 +- scripts/test-docker-all.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 6127829d..3480470c 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "prepublishOnly": "npm run build", "local-proxy": "node ./dist/run_locally.js", "test": "nyc cross-env NODE_OPTIONS=--insecure-http-parser mocha", - "test:docker": "docker build --tag proxy-chain-tests --file test/Dockerfile . && docker run proxy-chain-tests", + "test:docker": "docker build --tag proxy-chain-tests --file test/Dockerfile . && docker run --add-host localhost-test:127.0.0.1 proxy-chain-tests", "test:docker:all": "bash scripts/test-docker-all.sh", "lint": "eslint .", "lint:fix": "eslint . --fix" diff --git a/scripts/test-docker-all.sh b/scripts/test-docker-all.sh index 2df81ec0..f74721ca 100755 --- a/scripts/test-docker-all.sh +++ b/scripts/test-docker-all.sh @@ -3,11 +3,11 @@ echo "Starting parallel Docker tests for Node 14, 16, and 18..." # Run builds in parallel, capture PIDs. -docker build --build-arg NODE_IMAGE=node:14.21.3-bullseye --tag proxy-chain-tests:node14 --file test/Dockerfile . && docker run proxy-chain-tests:node14 & +docker build --build-arg NODE_IMAGE=node:14.21.3-bullseye --tag proxy-chain-tests:node14 --file test/Dockerfile . && docker run --add-host localhost-test:127.0.0.1 proxy-chain-tests:node14 & pid14=$! -docker build --build-arg NODE_IMAGE=node:16.20.2-bookworm --tag proxy-chain-tests:node16 --file test/Dockerfile . && docker run proxy-chain-tests:node16 & +docker build --build-arg NODE_IMAGE=node:16.20.2-bookworm --tag proxy-chain-tests:node16 --file test/Dockerfile . && docker run --add-host localhost-test:127.0.0.1 proxy-chain-tests:node16 & pid16=$! -docker build --build-arg NODE_IMAGE=node:18.20.8-bookworm --tag proxy-chain-tests:node18 --file test/Dockerfile . && docker run proxy-chain-tests:node18 & +docker build --build-arg NODE_IMAGE=node:18.20.8-bookworm --tag proxy-chain-tests:node18 --file test/Dockerfile . && docker run --add-host localhost-test:127.0.0.1 proxy-chain-tests:node18 & pid18=$! # Wait for all and capture exit codes. From 9659ff8f286bfaf428f434583d53dd1f2022b00a Mon Sep 17 00:00:00 2001 From: Yurii Bliuchak <1957659+bliuchak@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:08:32 +0100 Subject: [PATCH 2/2] fix(test): add try/finally cleanup to TLS handshake test to prevent hangs --- test/https-server.js | 199 +++++++++++++++++++++++-------------------- 1 file changed, 105 insertions(+), 94 deletions(-) diff --git a/test/https-server.js b/test/https-server.js index e9b5ffd6..8ee0ad82 100644 --- a/test/https-server.js +++ b/test/https-server.js @@ -14,128 +14,139 @@ it('handles TLS handshake failures gracefully and continues accepting connection this.timeout(10000); const tlsErrors = []; + let server; + let badSocket; + let goodSocket; - let server = new Server({ - port: 0, - serverType: 'https', - httpsOptions: { - key: sslKey, - cert: sslCrt, - }, - }); + try { + server = new Server({ + port: 0, + serverType: 'https', + httpsOptions: { + key: sslKey, + cert: sslCrt, + }, + }); - server.on('tlsError', ({ error }) => { - tlsErrors.push(error); - }); + server.on('tlsError', ({ error }) => { + tlsErrors.push(error); + }); - await server.listen(); - const serverPort = server.port; + await server.listen(); + const serverPort = server.port; - // Make invalid TLS connection. - const badSocket = tls.connect({ - port: serverPort, - host: '127.0.0.1', - rejectUnauthorized: false, - minVersion: 'TLSv1', - maxVersion: 'TLSv1', - }); + // Make invalid TLS connection. + badSocket = tls.connect({ + port: serverPort, + host: '127.0.0.1', + rejectUnauthorized: false, + minVersion: 'TLSv1', + maxVersion: 'TLSv1', + }); - const badSocketErrorOccurred = await new Promise((resolve, reject) => { - let errorOccurred = false; + const badSocketErrorOccurred = await new Promise((resolve, reject) => { + let errorOccurred = false; - badSocket.on('error', () => { - errorOccurred = true; - // Expected: TLS handshake will fail due to version mismatch. - }); + badSocket.on('error', () => { + errorOccurred = true; + // Expected: TLS handshake will fail due to version mismatch. + }); - badSocket.on('close', () => { - resolve(errorOccurred); - }); + badSocket.on('close', () => { + resolve(errorOccurred); + }); - badSocket.setTimeout(5000, () => { - badSocket.destroy(); - reject(new Error('Bad socket timed out before error')); - }); + badSocket.setTimeout(5000, () => { + badSocket.destroy(); + reject(new Error('Bad socket timed out before error')); + }); - }); + }); - await wait(100); + await wait(100); - expect(badSocketErrorOccurred).to.equal(true); + expect(badSocketErrorOccurred).to.equal(true); - // Make a valid TLS connection to prove server still works. - const goodSocket = tls.connect({ - port: serverPort, - host: '127.0.0.1', - rejectUnauthorized: false, - }); + // Make a valid TLS connection to prove server still works. + goodSocket = tls.connect({ + port: serverPort, + host: '127.0.0.1', + rejectUnauthorized: false, + }); - // Wait for secure connection. - const goodSocketConnected = await new Promise((resolve, reject) => { - let isConnected = false; + // Wait for secure connection. + const goodSocketConnected = await new Promise((resolve, reject) => { + let isConnected = false; - const timeout = setTimeout(() => { - goodSocket.destroy(); - reject(new Error('Good socket connection timed out')); - }, 5000); + const timeout = setTimeout(() => { + goodSocket.destroy(); + reject(new Error('Good socket connection timed out')); + }, 5000); - goodSocket.on('error', (err) => { - clearTimeout(timeout); - goodSocket.destroy(); - reject(err); - }); + goodSocket.on('error', (err) => { + clearTimeout(timeout); + goodSocket.destroy(); + reject(err); + }); - goodSocket.on('secureConnect', () => { - isConnected = true; - clearTimeout(timeout); - resolve(isConnected); - }); + goodSocket.on('secureConnect', () => { + isConnected = true; + clearTimeout(timeout); + resolve(isConnected); + }); - goodSocket.on('close', () => { - clearTimeout(timeout); + goodSocket.on('close', () => { + clearTimeout(timeout); + }); }); - }); - expect(goodSocketConnected).to.equal(true, 'Good socket should have connected'); + expect(goodSocketConnected).to.equal(true, 'Good socket should have connected'); - // Write the CONNECT request. - goodSocket.write('CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n'); + // Write the CONNECT request. + goodSocket.write('CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n'); - const response = await new Promise((resolve, reject) => { - const goodSocketTimeout = setTimeout(() => { - goodSocket.destroy(); - reject(new Error('Good socket connection timed out')); - }, 5000); + const response = await new Promise((resolve, reject) => { + const goodSocketTimeout = setTimeout(() => { + goodSocket.destroy(); + reject(new Error('Good socket connection timed out')); + }, 5000); - goodSocket.on('error', (err) => { - clearTimeout(goodSocketTimeout); - goodSocket.destroy(); - reject(err); - }); + goodSocket.on('error', (err) => { + clearTimeout(goodSocketTimeout); + goodSocket.destroy(); + reject(err); + }); - goodSocket.on('data', (data) => { - clearTimeout(goodSocketTimeout); - goodSocket.destroy(); - resolve(data.toString()); - }); + goodSocket.on('data', (data) => { + clearTimeout(goodSocketTimeout); + goodSocket.destroy(); + resolve(data.toString()); + }); - goodSocket.on('close', () => { - clearTimeout(goodSocketTimeout); + goodSocket.on('close', () => { + clearTimeout(goodSocketTimeout); + }); }); - }); - - await wait(100); - expect(response).to.be.equal('HTTP/1.1 200 Connection Established\r\n\r\n'); + await wait(100); - expect(tlsErrors.length).to.be.equal(1); - expect(tlsErrors[0].library).to.be.equal('SSL routines'); - expect(tlsErrors[0].reason).to.be.equal('unsupported protocol'); - expect(tlsErrors[0].code).to.be.equal('ERR_SSL_UNSUPPORTED_PROTOCOL'); + expect(response).to.be.equal('HTTP/1.1 200 Connection Established\r\n\r\n'); - // Cleanup. - server.close(true); - server = null; + expect(tlsErrors.length).to.be.equal(1); + expect(tlsErrors[0].library).to.be.equal('SSL routines'); + expect(tlsErrors[0].reason).to.be.equal('unsupported protocol'); + expect(tlsErrors[0].code).to.be.equal('ERR_SSL_UNSUPPORTED_PROTOCOL'); + } finally { + if (badSocket && !badSocket.destroyed) { + badSocket.destroy(); + } + if (goodSocket && !goodSocket.destroyed) { + goodSocket.destroy(); + } + if (server) { + await server.close(true); + } + } }); describe('HTTPS proxy server resource cleanup', () => {