import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as net from 'net'; import * as plugins from '../ts/plugins.js'; // Import SmartProxy and configurations import { SmartProxy } from '../ts/index.js'; tap.test('simple proxy chain test - identify connection accumulation', async () => { console.log('\n=== Simple Proxy Chain Test ==='); console.log('Setup: Client → SmartProxy1 (8590) → SmartProxy2 (8591) → Backend (down)'); // Create backend server that accepts and immediately closes connections const backend = net.createServer((socket) => { console.log('Backend: Connection received, closing immediately'); socket.destroy(); }); await new Promise((resolve) => { backend.listen(9998, () => { console.log('✓ Backend server started on port 9998 (closes connections immediately)'); resolve(); }); }); // Create SmartProxy2 (downstream) const proxy2 = new SmartProxy({ ports: [8591], enableDetailedLogging: true, socketTimeout: 5000, routes: [{ name: 'to-backend', match: { ports: 8591 }, action: { type: 'forward', target: { host: 'localhost', port: 9998 // Backend that closes immediately } } }] }); // Create SmartProxy1 (upstream) const proxy1 = new SmartProxy({ ports: [8590], enableDetailedLogging: true, socketTimeout: 5000, routes: [{ name: 'to-proxy2', match: { ports: 8590 }, action: { type: 'forward', target: { host: 'localhost', port: 8591 // Forward to proxy2 } } }] }); await proxy2.start(); console.log('✓ SmartProxy2 started on port 8591'); await proxy1.start(); console.log('✓ SmartProxy1 started on port 8590'); // Helper to get connection counts const getConnectionCounts = () => { const conn1 = (proxy1 as any).connectionManager; const conn2 = (proxy2 as any).connectionManager; return { proxy1: conn1 ? conn1.getConnectionCount() : 0, proxy2: conn2 ? conn2.getConnectionCount() : 0 }; }; console.log('\n--- Making 5 sequential connections ---'); for (let i = 0; i < 5; i++) { console.log(`\n=== Connection ${i + 1} ===`); const counts = getConnectionCounts(); console.log(`Before: Proxy1=${counts.proxy1}, Proxy2=${counts.proxy2}`); await new Promise((resolve) => { const client = new net.Socket(); let dataReceived = false; client.on('data', (data) => { console.log(`Client received data: ${data.toString()}`); dataReceived = true; }); client.on('error', (err) => { console.log(`Client error: ${err.code}`); resolve(); }); client.on('close', () => { console.log(`Client closed (data received: ${dataReceived})`); resolve(); }); client.connect(8590, 'localhost', () => { console.log('Client connected to Proxy1'); // Send HTTP request client.write('GET / HTTP/1.1\r\nHost: test.com\r\n\r\n'); }); // Timeout setTimeout(() => { if (!client.destroyed) { console.log('Client timeout, destroying'); client.destroy(); } resolve(); }, 2000); }); // Wait a bit and check counts await new Promise(resolve => setTimeout(resolve, 500)); const afterCounts = getConnectionCounts(); console.log(`After: Proxy1=${afterCounts.proxy1}, Proxy2=${afterCounts.proxy2}`); if (afterCounts.proxy1 > 0 || afterCounts.proxy2 > 0) { console.log('⚠️ WARNING: Connections not cleaned up!'); } } console.log('\n--- Test with backend completely down ---'); // Stop backend backend.close(); await new Promise(resolve => setTimeout(resolve, 100)); console.log('✓ Backend stopped'); // Make more connections with backend down for (let i = 0; i < 3; i++) { console.log(`\n=== Connection ${i + 6} (backend down) ===`); const counts = getConnectionCounts(); console.log(`Before: Proxy1=${counts.proxy1}, Proxy2=${counts.proxy2}`); await new Promise((resolve) => { const client = new net.Socket(); client.on('error', () => { resolve(); }); client.on('close', () => { resolve(); }); client.connect(8590, 'localhost', () => { client.write('GET / HTTP/1.1\r\nHost: test.com\r\n\r\n'); }); setTimeout(() => { if (!client.destroyed) { client.destroy(); } resolve(); }, 1000); }); await new Promise(resolve => setTimeout(resolve, 500)); const afterCounts = getConnectionCounts(); console.log(`After: Proxy1=${afterCounts.proxy1}, Proxy2=${afterCounts.proxy2}`); } // Final check console.log('\n--- Final Check ---'); await new Promise(resolve => setTimeout(resolve, 1000)); const finalCounts = getConnectionCounts(); console.log(`Final counts: Proxy1=${finalCounts.proxy1}, Proxy2=${finalCounts.proxy2}`); await proxy1.stop(); await proxy2.stop(); // Verify if (finalCounts.proxy1 > 0 || finalCounts.proxy2 > 0) { console.log('\n❌ FAIL: Connections accumulated!'); } else { console.log('\n✅ PASS: No connection accumulation'); } expect(finalCounts.proxy1).toEqual(0); expect(finalCounts.proxy2).toEqual(0); }); tap.start();