diff --git a/package-lock.json b/package-lock.json index d9c7819a4..2c47d894d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2655,7 +2655,7 @@ "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", @@ -2670,12 +2670,12 @@ "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -2684,27 +2684,27 @@ "node_modules/@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@sinonjs/commons": { "version": "1.8.3", @@ -9918,9 +9918,9 @@ } }, "node_modules/protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -13598,7 +13598,7 @@ "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, "@protobufjs/base64": { "version": "1.1.2", @@ -13613,12 +13613,12 @@ "@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" }, "@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "requires": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -13627,27 +13627,27 @@ "@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" }, "@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" }, "@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "@sinonjs/commons": { "version": "1.8.3", @@ -19083,9 +19083,9 @@ } }, "protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", diff --git a/tests/network/Proxy.test.ts b/tests/network/Proxy.test.ts index 3d32c3f51..f199f7a0b 100644 --- a/tests/network/Proxy.test.ts +++ b/tests/network/Proxy.test.ts @@ -6,12 +6,12 @@ import http from 'http'; import tls from 'tls'; import UTP from 'utp-native'; import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; -import { poll, promise, promisify, timerStart, timerStop } from '@/utils'; import Proxy from '@/network/Proxy'; import * as networkUtils from '@/network/utils'; import * as networkErrors from '@/network/errors'; import * as keysUtils from '@/keys/utils'; import * as nodesUtils from '@/nodes/utils'; +import { poll, promise, promisify, timerStart, timerStop } from '@/utils'; import * as testUtils from '../utils'; /** @@ -107,7 +107,7 @@ function tcpServer(end: boolean = false) { describe(Proxy.name, () => { const localHost = '127.0.0.1' as Host; const port = 0 as Port; - const logger = new Logger(`${Proxy.name} test`, LogLevel.DEBUG, [ + const logger = new Logger(`${Proxy.name} test`, LogLevel.WARN, [ new StreamHandler(), ]); const nodeIdABC = testUtils.generateRandomNodeId(); @@ -141,6 +141,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -157,6 +159,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -170,6 +174,7 @@ describe(Proxy.name, () => { // Start it again await proxy.start({ forwardHost: '::1' as Host, + proxyHost: localHost, serverHost: localHost, serverPort: port, tlsConfig: { @@ -193,6 +198,7 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, serverHost: localHost, serverPort: port, }); @@ -244,6 +250,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -278,6 +286,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -336,6 +346,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -395,6 +407,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -473,18 +487,18 @@ describe(Proxy.name, () => { ), ).rejects.toThrow(networkErrors.ErrorConnectionStart); await expect(remoteClosedP).resolves.toBeUndefined(); - expect(utpConnError).not.toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); // The TLS socket throw an error because there's no suitable signature algorithm - expect(tlsSocketError).toHaveBeenCalled(); + expect(tlsSocketError).toHaveBeenCalledTimes(1); // Expect(tlsSocketError.mock.calls[0][0]).toBeInstanceOf(Error); expect(tlsSocketError.mock.calls[0][0]).toHaveProperty( 'code', 'ERR_SSL_NO_SUITABLE_SIGNATURE_ALGORITHM', ); // The TLS socket end event never was emitted - expect(tlsSocketEnd).not.toHaveBeenCalled(); + expect(tlsSocketEnd).toHaveBeenCalledTimes(0); // The TLS socket close event is emitted with error - expect(tlsSocketClose).toHaveBeenCalled(); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(true); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -501,6 +515,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -582,18 +598,18 @@ describe(Proxy.name, () => { ), ).rejects.toThrow('502'); await expect(remoteClosedP).resolves.toBeUndefined(); - expect(utpConnError).not.toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); // The TLS socket throw an error because there's no suitable signature algorithm - expect(tlsSocketError).toHaveBeenCalled(); + expect(tlsSocketError).toHaveBeenCalledTimes(1); // Expect(tlsSocketError.mock.calls[0][0]).toBeInstanceOf(Error); expect(tlsSocketError.mock.calls[0][0]).toHaveProperty( 'code', 'ERR_SSL_NO_SUITABLE_SIGNATURE_ALGORITHM', ); // The TLS socket end event never was emitted - expect(tlsSocketEnd).not.toHaveBeenCalled(); + expect(tlsSocketEnd).toHaveBeenCalledTimes(0); // The TLS socket close event is emitted with error - expect(tlsSocketClose).toHaveBeenCalled(); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(true); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -619,6 +635,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -635,8 +653,17 @@ describe(Proxy.name, () => { let secured = false; const utpSocket = UTP.createServer(async (utpConn) => { utpConn.on('error', (e) => { + logger.warn('utpConn threw: ' + e.message); + // UTP implementation is buggy, + // we sometimes expect to see write after end error + if (e.message === 'Cannot call write after a stream was destroyed') { + return; + } utpConnError(e); }); + utpConn.on('end', async () => { + utpConn.destroy(); + }); const tlsSocket = new tls.TLSSocket(utpConn, { key: Buffer.from(serverKeyPairPem.privateKey, 'ascii'), cert: Buffer.from(serverCertPem, 'ascii'), @@ -702,13 +729,14 @@ describe(Proxy.name, () => { expect(secured).toBe(true); expect(proxy.getConnectionForwardCount()).toBe(0); await expect(remoteClosedP).resolves.toBeUndefined(); + expect(utpConnError).toHaveBeenCalledTimes(0); // No TLS socket errors this time // The client side figured that the node id is incorrect - expect(tlsSocketError).not.toHaveBeenCalled(); + expect(tlsSocketError).toHaveBeenCalledTimes(0); // This time the tls socket is ended from the client side - expect(tlsSocketEnd).toHaveBeenCalled(); + expect(tlsSocketEnd).toHaveBeenCalledTimes(1); // The TLS socket close event is emitted without error - expect(tlsSocketClose).toHaveBeenCalled(); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(false); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -734,6 +762,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -750,9 +780,17 @@ describe(Proxy.name, () => { let secured = false; const utpSocket = UTP.createServer(async (utpConn) => { utpConn.on('error', (e) => { - logger.debug(`utp connection error! ${e.message}`); + logger.warn('utpConn threw: ' + e.message); + // UTP implementation is buggy, + // we sometimes expect to see write after end error + if (e.message === 'Cannot call write after a stream was destroyed') { + return; + } utpConnError(e); }); + utpConn.on('end', async () => { + utpConn.destroy(); + }); const tlsSocket = new tls.TLSSocket(utpConn, { key: Buffer.from(serverKeyPairPem.privateKey, 'ascii'), cert: Buffer.from(serverCertPem, 'ascii'), @@ -761,11 +799,9 @@ describe(Proxy.name, () => { rejectUnauthorized: false, }); tlsSocket.on('secure', () => { - logger.debug('secure'); secured = true; }); tlsSocket.on('error', (e) => { - logger.debug(`tlsSocket error: ${e.message}`); tlsSocketError(e); }); tlsSocket.on('end', () => { @@ -781,11 +817,9 @@ describe(Proxy.name, () => { } }); tlsSocket.on('close', (hadError) => { - logger.debug('close'); tlsSocketClose(hadError); resolveRemoteClosedP(); }); - logger.debug('lol'); await send(networkUtils.pingBuffer); const punchInterval = setInterval(async () => { await send(networkUtils.pingBuffer); @@ -793,28 +827,24 @@ describe(Proxy.name, () => { await remoteReadyP; clearInterval(punchInterval); }); - const send = async (data: Buffer) => { - logger.debug(`sending message: ${data}`); - const utpSocketSend = promisify(utpSocket.send).bind(utpSocket); - await utpSocketSend(data, 0, data.byteLength, proxyPort, proxyHost); - }; const handleMessage = async (data: Buffer) => { const msg = networkUtils.unserializeNetworkMessage(data); - logger.debug(`received message: ${JSON.stringify(msg)}`); if (msg.type === 'ping') { - logger.debug('sending pong'); await send(networkUtils.pongBuffer); } else if (msg.type === 'pong') { resolveRemoteReadyP(); } }; utpSocket.on('message', handleMessage); + const send = async (data: Buffer) => { + const utpSocketSend = promisify(utpSocket.send).bind(utpSocket); + await utpSocketSend(data, 0, data.byteLength, proxyPort, proxyHost); + }; const utpSocketListen = promisify(utpSocket.listen).bind(utpSocket); await utpSocketListen(0, localHost); const utpSocketHost = utpSocket.address().address; const utpSocketPort = utpSocket.address().port; expect(proxy.getConnectionForwardCount()).toBe(0); - logger.debug('connecting'); await expect(() => httpConnect( proxy.getForwardHost(), @@ -825,17 +855,16 @@ describe(Proxy.name, () => { )}`, ), ).rejects.toThrow('526'); - logger.debug('connected'); await expect(remoteReadyP).resolves.toBeUndefined(); expect(secured).toBe(true); expect(proxy.getConnectionForwardCount()).toBe(0); await expect(remoteClosedP).resolves.toBeUndefined(); - // Expect(utpConnError).not.toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); // No TLS socket errors this time // The client side figured that the node id is incorrect - expect(tlsSocketError).not.toHaveBeenCalled(); + expect(tlsSocketError).toHaveBeenCalledTimes(0); // This time the tls socket is ended from the client side - expect(tlsSocketEnd).toHaveBeenCalled(); + expect(tlsSocketEnd).toHaveBeenCalledTimes(1); // The TLS socket close event is emitted without error expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(false); @@ -866,6 +895,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -882,11 +913,18 @@ describe(Proxy.name, () => { const tlsSocketClose = jest.fn(); // This UTP server will hold the connection const utpSocket = UTP.createServer(async (utpConn) => { - const utpLogger = logger.getChild('utpSocket'); utpConn.on('error', (e) => { - utpLogger.debug(`error: ${e.message}`); + logger.warn('utpConn threw: ' + e.message); + // UTP implementation is buggy, + // we sometimes expect to see write after end error + if (e.message === 'Cannot call write after a stream was destroyed') { + return; + } utpConnError(e); }); + utpConn.on('end', async () => { + utpConn.destroy(); + }); const tlsSocket = new tls.TLSSocket(utpConn, { key: Buffer.from(serverKeyPairPem.privateKey, 'ascii'), cert: Buffer.from(serverCertPem, 'ascii'), @@ -894,17 +932,14 @@ describe(Proxy.name, () => { requestCert: true, rejectUnauthorized: false, }); - const tlsLogger = utpLogger.getChild('tlsSocket'); tlsSocket.on('secure', () => { - tlsLogger.debug('secure'); resolveRemoteSecureP(); }); tlsSocket.on('error', (e) => { - tlsLogger.debug(`error: ${e.message}`); tlsSocketError(e); }); tlsSocket.on('end', () => { - tlsLogger.debug('Reverse: receives tlsSocket ending'); + logger.debug('Reverse: receives tlsSocket ending'); tlsSocketEnd(); if (utpConn.destroyed) { logger.debug('Reverse: destroys tlsSocket'); @@ -917,14 +952,11 @@ describe(Proxy.name, () => { } }); tlsSocket.on('close', (hadError) => { - tlsLogger.debug('close'); tlsSocketClose(hadError); resolveRemoteClosedP(); }); - logger.debug('sending ping'); await send(networkUtils.pingBuffer); const punchInterval = setInterval(async () => { - logger.debug('sending ping'); await send(networkUtils.pingBuffer); }, 1000); await remoteReadyP; @@ -933,26 +965,21 @@ describe(Proxy.name, () => { const handleMessage = async (data: Buffer) => { const msg = networkUtils.unserializeNetworkMessage(data); if (msg.type === 'ping') { - logger.debug('sending pong'); await send(networkUtils.pongBuffer); } else if (msg.type === 'pong') { - logger.debug('received pong'); resolveRemoteReadyP(); } }; utpSocket.on('message', handleMessage); const send = async (data: Buffer) => { - logger.debug('sending'); const utpSocketSend = promisify(utpSocket.send).bind(utpSocket); await utpSocketSend(data, 0, data.byteLength, proxyPort, proxyHost); }; const utpSocketListen = promisify(utpSocket.listen).bind(utpSocket); - logger.debug('listening'); await utpSocketListen(0, localHost); const utpSocketHost = utpSocket.address().address; const utpSocketPort = utpSocket.address().port; expect(proxy.getConnectionForwardCount()).toBe(0); - logger.debug('opening forward connection'); await proxy.openConnectionForward( serverNodeId, utpSocketHost as Host, @@ -961,7 +988,6 @@ describe(Proxy.name, () => { await expect(remoteReadyP).resolves.toBeUndefined(); await expect(remoteSecureP).resolves.toBeUndefined(); // Opening a duplicate connection is noop - logger.debug('opening 2nd forward connection'); await proxy.openConnectionForward( serverNodeId, utpSocketHost as Host, @@ -974,10 +1000,10 @@ describe(Proxy.name, () => { ); expect(proxy.getConnectionForwardCount()).toBe(0); await expect(remoteClosedP).resolves.toBeUndefined(); - // Expect(utpConnError).not.toHaveBeenCalled(); - expect(tlsSocketError).not.toHaveBeenCalled(); - expect(tlsSocketEnd).toHaveBeenCalled(); - expect(tlsSocketClose).toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); + expect(tlsSocketError).toHaveBeenCalledTimes(0); + expect(tlsSocketEnd).toHaveBeenCalledTimes(1); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(false); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -1007,6 +1033,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -1121,12 +1149,12 @@ describe(Proxy.name, () => { 100, ), ).resolves.toBe(0); - expect(utpConnError).not.toHaveBeenCalled(); - expect(tlsSocketError).not.toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); + expect(tlsSocketError).toHaveBeenCalledTimes(0); // This time the reverse side initiates the end // Therefore, this handler is removed - expect(tlsSocketEnd).not.toHaveBeenCalled(); - expect(tlsSocketClose).toHaveBeenCalled(); + expect(tlsSocketEnd).toHaveBeenCalledTimes(0); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(false); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -1156,6 +1184,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -1173,8 +1203,17 @@ describe(Proxy.name, () => { // This UTP server will hold the connection const utpSocket = UTP.createServer(async (utpConn) => { utpConn.on('error', (e) => { + logger.warn('utpConn threw: ' + e.message); + // UTP implementation is buggy, + // we sometimes expect to see write after end error + if (e.message === 'Cannot call write after a stream was destroyed') { + return; + } utpConnError(e); }); + utpConn.on('end', async () => { + utpConn.destroy(); + }); const tlsSocket = new tls.TLSSocket(utpConn, { key: Buffer.from(serverKeyPairPem.privateKey, 'ascii'), cert: Buffer.from(serverCertPem, 'ascii'), @@ -1269,13 +1308,13 @@ describe(Proxy.name, () => { utpSocketPort as Port, ); expect(proxy.getConnectionForwardCount()).toBe(0); - expect(clientSocketEnd).toHaveBeenCalled(); + expect(clientSocketEnd).toHaveBeenCalledTimes(1); await expect(localClosedP).resolves.toBeUndefined(); await expect(remoteClosedP).resolves.toBeUndefined(); - // Expect(utpConnError).not.toHaveBeenCalled(); - expect(tlsSocketError).not.toHaveBeenCalled(); - expect(tlsSocketEnd).toHaveBeenCalled(); - expect(tlsSocketClose).toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); + expect(tlsSocketError).toHaveBeenCalledTimes(0); + expect(tlsSocketEnd).toHaveBeenCalledTimes(1); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(false); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -1305,6 +1344,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -1427,7 +1468,7 @@ describe(Proxy.name, () => { tlsSocket_!.destroy(); logger.debug('Reverse: finishes tlsSocket ending'); await expect(localClosedP).resolves.toBeUndefined(); - expect(clientSocketEnd).toHaveBeenCalled(); + expect(clientSocketEnd).toHaveBeenCalledTimes(1); await expect(remoteClosedP).resolves.toBeUndefined(); // Connection count should reach 0 eventually await expect( @@ -1441,12 +1482,12 @@ describe(Proxy.name, () => { 100, ), ).resolves.toBe(0); - expect(utpConnError).not.toHaveBeenCalled(); - expect(tlsSocketError).not.toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); + expect(tlsSocketError).toHaveBeenCalledTimes(0); // This time the reverse side initiates the end // Therefore, this handler is removed - expect(tlsSocketEnd).not.toHaveBeenCalled(); - expect(tlsSocketClose).toHaveBeenCalled(); + expect(tlsSocketEnd).toHaveBeenCalledTimes(0); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(false); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -1476,6 +1517,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -1601,10 +1644,10 @@ describe(Proxy.name, () => { 100, ), ).resolves.toBe(0); - expect(utpConnError).not.toHaveBeenCalled(); - expect(tlsSocketError).not.toHaveBeenCalled(); - expect(tlsSocketEnd).toHaveBeenCalled(); - expect(tlsSocketClose).toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); + expect(tlsSocketError).toHaveBeenCalledTimes(0); + expect(tlsSocketEnd).toHaveBeenCalledTimes(1); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(false); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -1632,6 +1675,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -1732,10 +1777,10 @@ describe(Proxy.name, () => { ); await expect(localClosedP).resolves.toBeUndefined(); await expect(remoteClosedP).resolves.toBeUndefined(); - expect(utpConnError).not.toHaveBeenCalled(); - expect(tlsSocketError).not.toHaveBeenCalled(); - expect(tlsSocketEnd).toHaveBeenCalled(); - expect(tlsSocketClose).toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); + expect(tlsSocketError).toHaveBeenCalledTimes(0); + expect(tlsSocketEnd).toHaveBeenCalledTimes(1); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(false); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -1764,6 +1809,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -1849,10 +1896,10 @@ describe(Proxy.name, () => { // When ErrorConnectionTimeout is triggered // This results in the destruction of the socket await expect(remoteClosedP).resolves.toBeUndefined(); - expect(utpConnError).not.toHaveBeenCalled(); - expect(tlsSocketError).not.toHaveBeenCalled(); - expect(tlsSocketEnd).toHaveBeenCalled(); - expect(tlsSocketClose).toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); + expect(tlsSocketError).toHaveBeenCalledTimes(0); + expect(tlsSocketEnd).toHaveBeenCalledTimes(1); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(false); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -1882,6 +1929,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -1990,10 +2039,10 @@ describe(Proxy.name, () => { 100, ), ).resolves.toBe(0); - expect(utpConnError).not.toHaveBeenCalled(); - expect(tlsSocketError).not.toHaveBeenCalled(); - expect(tlsSocketEnd).toHaveBeenCalled(); - expect(tlsSocketClose).toHaveBeenCalled(); + expect(utpConnError).toHaveBeenCalledTimes(0); + expect(tlsSocketError).toHaveBeenCalledTimes(0); + expect(tlsSocketEnd).toHaveBeenCalledTimes(1); + expect(tlsSocketClose).toHaveBeenCalledTimes(1); expect(tlsSocketClose).toHaveBeenCalledWith(false); utpSocket.off('message', handleMessage); utpSocket.close(); @@ -2020,6 +2069,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -2127,6 +2178,8 @@ describe(Proxy.name, () => { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, }, + proxyHost: localHost, + forwardHost: localHost, serverHost: localHost, serverPort: port, }); @@ -2278,7 +2331,8 @@ describe(Proxy.name, () => { await proxy.start({ serverHost: serverHost(), serverPort: serverPort(), - + proxyHost: localHost, + forwardHost: localHost, tlsConfig: { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, @@ -2309,7 +2363,8 @@ describe(Proxy.name, () => { await proxy.start({ serverHost: serverHost(), serverPort: serverPort(), - + proxyHost: localHost, + forwardHost: localHost, tlsConfig: { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, @@ -2349,6 +2404,8 @@ describe(Proxy.name, () => { await proxy.start({ serverHost: serverHost(), serverPort: serverPort(), + proxyHost: localHost, + forwardHost: localHost, tlsConfig: { keyPrivatePem: keyPairPem.privateKey, @@ -2400,6 +2457,8 @@ describe(Proxy.name, () => { await proxy.start({ serverHost: serverHost(), serverPort: serverPort(), + proxyHost: localHost, + forwardHost: localHost, tlsConfig: { keyPrivatePem: keyPairPem.privateKey, @@ -2476,6 +2535,8 @@ describe(Proxy.name, () => { await proxy.start({ serverHost: serverHost(), serverPort: serverPort(), + proxyHost: localHost, + forwardHost: localHost, tlsConfig: { keyPrivatePem: keyPairPem.privateKey, @@ -2541,8 +2602,8 @@ describe(Proxy.name, () => { await proxy.start({ serverHost: serverHost(), serverPort: serverPort(), - proxyHost: localHost, forwardHost: localHost, + proxyHost: localHost, tlsConfig: { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, @@ -2570,8 +2631,7 @@ describe(Proxy.name, () => { // This retries multiple times // This will eventually fail and trigger a ErrorConnectionComposeTimeout const utpConn = utpSocket.connect(proxyPort, proxyHost); - utpConn.setTimeout(2000); - utpConn.on('timeout', () => { + utpConn.setTimeout(2000, () => { utpConn.emit('error', new Error('TIMED OUT')); }); const { p: utpConnClosedP, resolveP: resolveUtpConnClosedP } = @@ -2579,7 +2639,6 @@ describe(Proxy.name, () => { const { p: utpConnErrorP, rejectP: rejectUtpConnErrorP } = promise(); utpConn.on('error', (e) => { rejectUtpConnErrorP(e); - utpConn.end(); utpConn.destroy(); }); utpConn.on('close', () => { @@ -2637,6 +2696,7 @@ describe(Proxy.name, () => { certChainPem: certPem, }, proxyHost: localHost, + forwardHost: localHost, }); const externalHost = proxy.getProxyHost(); const externalPort = proxy.getProxyPort(); @@ -2743,6 +2803,7 @@ describe(Proxy.name, () => { serverHost: serverHost(), serverPort: serverPort(), proxyHost: localHost, + forwardHost: localHost, tlsConfig: { keyPrivatePem: keyPairPem.privateKey, certChainPem: certPem, diff --git a/tests/network/utils.test.ts b/tests/network/utils.test.ts index 4ef705fd8..d1a26f6aa 100644 --- a/tests/network/utils.test.ts +++ b/tests/network/utils.test.ts @@ -1,6 +1,6 @@ import type { Host, Port } from '@/network/types'; - -import { utils as networkUtils, errors as networkErrors } from '@/network'; +import * as networkUtils from '@/network/utils'; +import * as networkErrors from '@/network/errors'; describe('utils', () => { test('building addresses', async () => {