api-response-manager 2.6.5 ā 2.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/commands/tunnel.js +78 -10
- package/package.json +1 -1
package/README.md
CHANGED
package/commands/tunnel.js
CHANGED
|
@@ -153,17 +153,10 @@ async function connectTunnelClient(tunnelId, subdomain, localPort, protocol = 'h
|
|
|
153
153
|
console.log(chalk.white('Your local server is now accessible at:'));
|
|
154
154
|
console.log(chalk.cyan.bold(` ${message.publicUrl}\n`));
|
|
155
155
|
|
|
156
|
-
// Show TCP/SSH specific info
|
|
156
|
+
// Show TCP/SSH specific info and start local proxy
|
|
157
157
|
if (isTcpTunnel && message.tcpHost && message.tcpPort) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
console.log(chalk.white(` TCP Port: ${message.tcpPort}\n`));
|
|
161
|
-
|
|
162
|
-
console.log(chalk.gray('Connect via SSH:'));
|
|
163
|
-
console.log(chalk.cyan(` ssh <username>@${message.tcpHost} -p ${message.tcpPort}\n`));
|
|
164
|
-
console.log(chalk.gray('SSH with Key:'));
|
|
165
|
-
console.log(chalk.cyan(` ssh -i ~/.ssh/your_key <username>@${message.tcpHost} -p ${message.tcpPort}\n`));
|
|
166
|
-
console.log(chalk.gray.italic(' Replace <username> with your device\'s SSH username\n'));
|
|
158
|
+
// Start a local TCP proxy that transparently handles subdomain multiplexing
|
|
159
|
+
startLocalTcpProxy(subdomain, message.tcpHost, message.tcpPort, silent);
|
|
167
160
|
} else {
|
|
168
161
|
// Display QR code for HTTP/HTTPS tunnels (not for TCP/SSH)
|
|
169
162
|
displayQRCode(message.publicUrl);
|
|
@@ -282,6 +275,11 @@ async function connectTunnelClient(tunnelId, subdomain, localPort, protocol = 'h
|
|
|
282
275
|
process.on('SIGINT', () => {
|
|
283
276
|
if (heartbeatInterval) clearInterval(heartbeatInterval);
|
|
284
277
|
console.log(chalk.yellow('\n\nā Stopping tunnel...'));
|
|
278
|
+
// Close local TCP proxy if running
|
|
279
|
+
if (localProxyServer) {
|
|
280
|
+
localProxyServer.close();
|
|
281
|
+
localProxyServer = null;
|
|
282
|
+
}
|
|
285
283
|
ws.close();
|
|
286
284
|
});
|
|
287
285
|
}
|
|
@@ -563,6 +561,76 @@ async function configureIngress(tunnelId, rules, options) {
|
|
|
563
561
|
}
|
|
564
562
|
}
|
|
565
563
|
|
|
564
|
+
// Local TCP proxy for SSH/TCP tunnels (ngrok-style)
|
|
565
|
+
// Starts a local TCP server that transparently handles subdomain multiplexing.
|
|
566
|
+
// User just connects to localhost:<localProxyPort> ā the proxy injects the
|
|
567
|
+
// SUBDOMAIN header and relays all data bidirectionally to the tunnel server.
|
|
568
|
+
let localProxyServer = null;
|
|
569
|
+
|
|
570
|
+
function startLocalTcpProxy(subdomain, remoteHost, remotePort, silent) {
|
|
571
|
+
localProxyServer = net.createServer((clientSocket) => {
|
|
572
|
+
if (!silent) {
|
|
573
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
574
|
+
console.log(chalk.gray(`[${timestamp}]`), chalk.magenta('SSH'), chalk.white('New connection via local proxy'));
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Connect to the tunnel server's TCP port
|
|
578
|
+
const remoteSocket = net.createConnection({
|
|
579
|
+
host: remoteHost,
|
|
580
|
+
port: remotePort
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
remoteSocket.on('connect', () => {
|
|
584
|
+
// Send subdomain header first, then pipe everything bidirectionally
|
|
585
|
+
remoteSocket.write(`SUBDOMAIN:${subdomain}\n`, () => {
|
|
586
|
+
// After header is sent, relay data both ways
|
|
587
|
+
clientSocket.pipe(remoteSocket, { end: true });
|
|
588
|
+
remoteSocket.pipe(clientSocket, { end: true });
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
remoteSocket.on('error', (error) => {
|
|
593
|
+
if (!silent) {
|
|
594
|
+
console.error(chalk.red(` Proxy remote error: ${error.message}`));
|
|
595
|
+
}
|
|
596
|
+
clientSocket.end();
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
clientSocket.on('error', (error) => {
|
|
600
|
+
if (!silent && error.code !== 'ECONNRESET') {
|
|
601
|
+
console.error(chalk.red(` Proxy client error: ${error.message}`));
|
|
602
|
+
}
|
|
603
|
+
remoteSocket.end();
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
clientSocket.on('end', () => {
|
|
607
|
+
remoteSocket.end();
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
remoteSocket.on('end', () => {
|
|
611
|
+
clientSocket.end();
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
// Listen on port 0 to get a random available port
|
|
616
|
+
localProxyServer.listen(0, '127.0.0.1', () => {
|
|
617
|
+
const localPort = localProxyServer.address().port;
|
|
618
|
+
|
|
619
|
+
if (!silent) {
|
|
620
|
+
console.log(chalk.yellow.bold('š SSH/TCP Tunnel Ready!\n'));
|
|
621
|
+
console.log(chalk.white(' Connect via SSH:\n'));
|
|
622
|
+
console.log(chalk.cyan.bold(` ssh <username>@localhost -p ${localPort}\n`));
|
|
623
|
+
console.log(chalk.gray(' SSH with Key:'));
|
|
624
|
+
console.log(chalk.cyan(` ssh -i ~/.ssh/your_key <username>@localhost -p ${localPort}\n`));
|
|
625
|
+
console.log(chalk.gray.italic(` Replace <username> with your device's SSH username\n`));
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
localProxyServer.on('error', (error) => {
|
|
630
|
+
console.error(chalk.red(`Local proxy error: ${error.message}`));
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
566
634
|
// TCP connection handlers for SSH/TCP tunnels
|
|
567
635
|
function handleTcpConnect(message, localPort, ws, silent) {
|
|
568
636
|
const { connectionId, remoteAddress, remotePort } = message;
|