Skip to content

Commit 5534c5d

Browse files
authored
fix: correct localhost-test resolving on linux machines (apify#636)
* fix: correct localhost-test resolving on linux machines * fix(test): add try/finally cleanup to TLS handshake test to prevent hangs
1 parent a9449bc commit 5534c5d

File tree

3 files changed

+109
-98
lines changed

3 files changed

+109
-98
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"prepublishOnly": "npm run build",
3939
"local-proxy": "node ./dist/run_locally.js",
4040
"test": "nyc cross-env NODE_OPTIONS=--insecure-http-parser mocha",
41-
"test:docker": "docker build --tag proxy-chain-tests --file test/Dockerfile . && docker run proxy-chain-tests",
41+
"test:docker": "docker build --tag proxy-chain-tests --file test/Dockerfile . && docker run --add-host localhost-test:127.0.0.1 proxy-chain-tests",
4242
"test:docker:all": "bash scripts/test-docker-all.sh",
4343
"lint": "eslint .",
4444
"lint:fix": "eslint . --fix"

scripts/test-docker-all.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
echo "Starting parallel Docker tests for Node 14, 16, and 18..."
44

55
# Run builds in parallel, capture PIDs.
6-
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 &
6+
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 &
77
pid14=$!
8-
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 &
8+
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 &
99
pid16=$!
10-
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 &
10+
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 &
1111
pid18=$!
1212

1313
# Wait for all and capture exit codes.

test/https-server.js

Lines changed: 105 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -14,128 +14,139 @@ it('handles TLS handshake failures gracefully and continues accepting connection
1414
this.timeout(10000);
1515

1616
const tlsErrors = [];
17+
let server;
18+
let badSocket;
19+
let goodSocket;
1720

18-
let server = new Server({
19-
port: 0,
20-
serverType: 'https',
21-
httpsOptions: {
22-
key: sslKey,
23-
cert: sslCrt,
24-
},
25-
});
21+
try {
22+
server = new Server({
23+
port: 0,
24+
serverType: 'https',
25+
httpsOptions: {
26+
key: sslKey,
27+
cert: sslCrt,
28+
},
29+
});
2630

27-
server.on('tlsError', ({ error }) => {
28-
tlsErrors.push(error);
29-
});
31+
server.on('tlsError', ({ error }) => {
32+
tlsErrors.push(error);
33+
});
3034

31-
await server.listen();
32-
const serverPort = server.port;
35+
await server.listen();
36+
const serverPort = server.port;
3337

34-
// Make invalid TLS connection.
35-
const badSocket = tls.connect({
36-
port: serverPort,
37-
host: '127.0.0.1',
38-
rejectUnauthorized: false,
39-
minVersion: 'TLSv1',
40-
maxVersion: 'TLSv1',
41-
});
38+
// Make invalid TLS connection.
39+
badSocket = tls.connect({
40+
port: serverPort,
41+
host: '127.0.0.1',
42+
rejectUnauthorized: false,
43+
minVersion: 'TLSv1',
44+
maxVersion: 'TLSv1',
45+
});
4246

43-
const badSocketErrorOccurred = await new Promise((resolve, reject) => {
44-
let errorOccurred = false;
47+
const badSocketErrorOccurred = await new Promise((resolve, reject) => {
48+
let errorOccurred = false;
4549

46-
badSocket.on('error', () => {
47-
errorOccurred = true;
48-
// Expected: TLS handshake will fail due to version mismatch.
49-
});
50+
badSocket.on('error', () => {
51+
errorOccurred = true;
52+
// Expected: TLS handshake will fail due to version mismatch.
53+
});
5054

51-
badSocket.on('close', () => {
52-
resolve(errorOccurred);
53-
});
55+
badSocket.on('close', () => {
56+
resolve(errorOccurred);
57+
});
5458

55-
badSocket.setTimeout(5000, () => {
56-
badSocket.destroy();
57-
reject(new Error('Bad socket timed out before error'));
58-
});
59+
badSocket.setTimeout(5000, () => {
60+
badSocket.destroy();
61+
reject(new Error('Bad socket timed out before error'));
62+
});
5963

60-
});
64+
});
6165

62-
await wait(100);
66+
await wait(100);
6367

64-
expect(badSocketErrorOccurred).to.equal(true);
68+
expect(badSocketErrorOccurred).to.equal(true);
6569

66-
// Make a valid TLS connection to prove server still works.
67-
const goodSocket = tls.connect({
68-
port: serverPort,
69-
host: '127.0.0.1',
70-
rejectUnauthorized: false,
71-
});
70+
// Make a valid TLS connection to prove server still works.
71+
goodSocket = tls.connect({
72+
port: serverPort,
73+
host: '127.0.0.1',
74+
rejectUnauthorized: false,
75+
});
7276

73-
// Wait for secure connection.
74-
const goodSocketConnected = await new Promise((resolve, reject) => {
75-
let isConnected = false;
77+
// Wait for secure connection.
78+
const goodSocketConnected = await new Promise((resolve, reject) => {
79+
let isConnected = false;
7680

77-
const timeout = setTimeout(() => {
78-
goodSocket.destroy();
79-
reject(new Error('Good socket connection timed out'));
80-
}, 5000);
81+
const timeout = setTimeout(() => {
82+
goodSocket.destroy();
83+
reject(new Error('Good socket connection timed out'));
84+
}, 5000);
8185

82-
goodSocket.on('error', (err) => {
83-
clearTimeout(timeout);
84-
goodSocket.destroy();
85-
reject(err);
86-
});
86+
goodSocket.on('error', (err) => {
87+
clearTimeout(timeout);
88+
goodSocket.destroy();
89+
reject(err);
90+
});
8791

88-
goodSocket.on('secureConnect', () => {
89-
isConnected = true;
90-
clearTimeout(timeout);
91-
resolve(isConnected);
92-
});
92+
goodSocket.on('secureConnect', () => {
93+
isConnected = true;
94+
clearTimeout(timeout);
95+
resolve(isConnected);
96+
});
9397

94-
goodSocket.on('close', () => {
95-
clearTimeout(timeout);
98+
goodSocket.on('close', () => {
99+
clearTimeout(timeout);
100+
});
96101
});
97-
});
98102

99-
expect(goodSocketConnected).to.equal(true, 'Good socket should have connected');
103+
expect(goodSocketConnected).to.equal(true, 'Good socket should have connected');
100104

101-
// Write the CONNECT request.
102-
goodSocket.write('CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n');
105+
// Write the CONNECT request.
106+
goodSocket.write('CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n');
103107

104-
const response = await new Promise((resolve, reject) => {
105-
const goodSocketTimeout = setTimeout(() => {
106-
goodSocket.destroy();
107-
reject(new Error('Good socket connection timed out'));
108-
}, 5000);
108+
const response = await new Promise((resolve, reject) => {
109+
const goodSocketTimeout = setTimeout(() => {
110+
goodSocket.destroy();
111+
reject(new Error('Good socket connection timed out'));
112+
}, 5000);
109113

110-
goodSocket.on('error', (err) => {
111-
clearTimeout(goodSocketTimeout);
112-
goodSocket.destroy();
113-
reject(err);
114-
});
114+
goodSocket.on('error', (err) => {
115+
clearTimeout(goodSocketTimeout);
116+
goodSocket.destroy();
117+
reject(err);
118+
});
115119

116-
goodSocket.on('data', (data) => {
117-
clearTimeout(goodSocketTimeout);
118-
goodSocket.destroy();
119-
resolve(data.toString());
120-
});
120+
goodSocket.on('data', (data) => {
121+
clearTimeout(goodSocketTimeout);
122+
goodSocket.destroy();
123+
resolve(data.toString());
124+
});
121125

122-
goodSocket.on('close', () => {
123-
clearTimeout(goodSocketTimeout);
126+
goodSocket.on('close', () => {
127+
clearTimeout(goodSocketTimeout);
128+
});
124129
});
125-
});
126-
127-
await wait(100);
128130

129-
expect(response).to.be.equal('HTTP/1.1 200 Connection Established\r\n\r\n');
131+
await wait(100);
130132

131-
expect(tlsErrors.length).to.be.equal(1);
132-
expect(tlsErrors[0].library).to.be.equal('SSL routines');
133-
expect(tlsErrors[0].reason).to.be.equal('unsupported protocol');
134-
expect(tlsErrors[0].code).to.be.equal('ERR_SSL_UNSUPPORTED_PROTOCOL');
133+
expect(response).to.be.equal('HTTP/1.1 200 Connection Established\r\n\r\n');
135134

136-
// Cleanup.
137-
server.close(true);
138-
server = null;
135+
expect(tlsErrors.length).to.be.equal(1);
136+
expect(tlsErrors[0].library).to.be.equal('SSL routines');
137+
expect(tlsErrors[0].reason).to.be.equal('unsupported protocol');
138+
expect(tlsErrors[0].code).to.be.equal('ERR_SSL_UNSUPPORTED_PROTOCOL');
139+
} finally {
140+
if (badSocket && !badSocket.destroyed) {
141+
badSocket.destroy();
142+
}
143+
if (goodSocket && !goodSocket.destroyed) {
144+
goodSocket.destroy();
145+
}
146+
if (server) {
147+
await server.close(true);
148+
}
149+
}
139150
});
140151

141152
describe('HTTPS proxy server resource cleanup', () => {

0 commit comments

Comments
 (0)