From dd68f2be30eaf8dcdd69dcd4f0442dc95a63cbe5 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchyshyn Date: Thu, 27 Jun 2024 14:01:16 +0300 Subject: [PATCH] fix: handle ETIMEDOUT error; add missing typings --- easyTunnel.d.ts | 4 +++- easyTunnel.spec.js | 8 +++++++- lib/Tunnel.js | 4 +++- lib/TunnelCluster.js | 7 +++---- package.json | 2 +- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/easyTunnel.d.ts b/easyTunnel.d.ts index 2f98a6d..d039b49 100644 --- a/easyTunnel.d.ts +++ b/easyTunnel.d.ts @@ -1,7 +1,9 @@ import {EventEmitter} from 'events'; -declare interface Tunnel extends EventEmitter { +export declare interface Tunnel extends EventEmitter { url: string; + clientId: string; + close(): void; } declare interface BootstrapOpts { diff --git a/easyTunnel.spec.js b/easyTunnel.spec.js index cca6d45..8464d2a 100644 --- a/easyTunnel.spec.js +++ b/easyTunnel.spec.js @@ -267,11 +267,16 @@ describe('localtunnel', () => { remoteSocket.close(); }); - it('handle --connect-timeout on initial request', async () => { + it('handle --connect-timeout on initial request (ECONNREFUSED)', async () => { const tunnel = easyTunnel({ port: fakePort, host: 'http://8.8.8.8', connect_timeout: 2000 }); await assert.rejects(tunnel, { message: 'timeout of 2000ms exceeded' }); }); + it('handle --connect-timeout on initial request (ETIMEDOUT)', async () => { + const tunnel = easyTunnel({ port: fakePort, host: 'http://172.30.68.144', connect_timeout: 2000 }); + await assert.rejects(tunnel, { message: 'timeout of 2000ms exceeded' }); + }); + it('handle --connect-timeout on socket connect', async () => { const maxSockets = 1; nock(fakeHost).get('/?new').reply(200, { @@ -279,6 +284,7 @@ describe('localtunnel', () => { port: tunnelPort, max_conn_count: maxSockets, is_tunnel_secure: false, + remote_ip: '8.8.8.8', url: 'https://test.localhost', }); diff --git a/lib/Tunnel.js b/lib/Tunnel.js index 802b9f5..feda2f0 100644 --- a/lib/Tunnel.js +++ b/lib/Tunnel.js @@ -15,6 +15,7 @@ module.exports = class Tunnel extends EventEmitter { if (!this.opts.host) { this.opts.host = 'http://localhost:8087'; } + this.opts.connect_timeout = this.opts.connect_timeout ?? 10_000; this.destroyTimer = null; } @@ -54,7 +55,7 @@ module.exports = class Tunnel extends EventEmitter { is_tunnel_secure, local_max_retries, local_reconnect_delay, - connect_timeout: connect_timeout ?? 10_000, + connect_timeout: connect_timeout, idle_timeout: idle_timeout ?? 15_000, }; /* eslint-enable camelcase */ @@ -224,5 +225,6 @@ module.exports = class Tunnel extends EventEmitter { destroy() { this.emit('error', new Error('Tunnel timed out')); + this.close(); } }; diff --git a/lib/TunnelCluster.js b/lib/TunnelCluster.js index 7e277c1..c2234cb 100644 --- a/lib/TunnelCluster.js +++ b/lib/TunnelCluster.js @@ -32,8 +32,7 @@ module.exports = class TunnelCluster extends EventEmitter { const localProtocol = opt.local_https ? 'https' : 'http'; const allowInvalidCert = opt.allow_invalid_cert; const isTunnelSecure = opt.is_tunnel_secure; - const localReconnectionMaxRetryCount = - opt.local_max_retries !== undefined ? opt.local_max_retries : Infinity; + const localReconnectionMaxRetryCount = opt.local_max_retries !== undefined ? opt.local_max_retries : Infinity; const localReconnectionDelay = opt.local_reconnect_delay !== undefined ? opt.local_reconnect_delay : 1000; debug( @@ -64,7 +63,7 @@ module.exports = class TunnelCluster extends EventEmitter { // emit connection refused errors immediately, because they // indicate that the tunnel can't be established. - if (err.code === 'ECONNREFUSED') { + if (['ECONNREFUSED', 'ETIMEDOUT'].includes(err.code)) { this.emit( 'error', new Error(`connection refused: ${remoteHostOrIp}:${remotePort} (check your firewall settings)`) @@ -140,7 +139,7 @@ module.exports = class TunnelCluster extends EventEmitter { pump(stream, local, remote, err => { debug('stream finished', err); - this.emit('dead'); + this.emit('dead', { idleMonitoring }); }); // when local closes, also get a new remote diff --git a/package.json b/package.json index 32b8417..dee676b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "et": "bin/et.js" }, "scripts": { - "dev:client": "nodemon --inspect ./bin/et.js --port 3001 --host http://lvh.me:8087 --subdomain test --open --idle-timeout=6000000", + "dev:client": "nodemon --inspect ./bin/et.js --port 3001 --host http://lvh.me:8087 --subdomain test --open", "dev:backend": "nodemon server.js", "dev": "DEBUG=mytunnel:* concurrently --raw npm:dev:backend npm:dev:client", "test": "mocha --timeout 15000 --exit -- *.spec.js"