There is a common example out there for creating a TCP proxy server in nodejs (I can't find my original source but its essentially a small adaptation of the echo server)
nodejs boils down to:
var net = require('net');
net.createServer(function(from) {
var to = net.createConnection({
host: '<toHost>',
port: <toPort>
});
from.on('error', (err) => {
to.destroy(err);
});
to.on('error', (err) => {
from.destroy(err);
});
from.pipe(to);
to.pipe(from);
}).listen('<someport>', undefined);
Works great. if you change it to import net from "node:net";
it works with the compatibility layer in Deno.
But I was trying to get it to work with Deno's native API. I went to the echo server example https://docs.deno.com/runtime/tutorials/tcp_echo and tried to do the same, perform a connection and pipe them together:
const listener = Deno.listen({
port: <fromPort>,
host: undefined
});
try{
for await (const conn1 of listener) {
try{
const conn2 = await Deno.connect({
host: '<toHost>',
port: <toPort>
});
if (conn2) {
conn1.readable.pipeTo(conn2.writable);
conn2.readable.pipeTo(conn1.writable);
}
} catch (e) { console.log(e); }
}} catch (e) { console.log(e); }
And it works! ...for about a minute. Then it exits with this error:
error: Uncaught (in promise) Interrupted: operation canceled
at async Object.pull (ext:deno_web/06_streams.js:1001:27)
I wrapped all the awaits with try-catches but it doesn't catch the issue. behaves identically with and without them. I don't see from the docs or the code how to handle this. If a client is canceling I wouldn't expect the server to go down.
Looks like the issue is that NodeJS connection.pipe
is not a promise but in Deno connection.pipeTo
is a promise so they need to be awaited to be caught with try-catch (though you want them to run in parallel so a promise all works)
The Interrupted error still happens when a connection gets cancelled but the server doesn't die.
const listener = Deno.listen({
port: <fromPort>,
host: undefined
});
try{
for await (const conn1 of listener) {
try{
const conn2 = await Deno.connect({
host: '<toHost>',
port: <toPort>
});
if (conn2) {
await Promise.all([
conn1.readable.pipeTo(conn2.writable),
conn2.readable.pipeTo(conn1.writable)
]);
}
} catch (e) { console.log(e); }
}} catch (e) { console.log(e); }