Skip to content

Instantly share code, notes, and snippets.

@snydergd
Created September 13, 2022 18:00
Show Gist options
  • Save snydergd/0bef6fedd035ad21d383bde31145c9a7 to your computer and use it in GitHub Desktop.
Save snydergd/0bef6fedd035ad21d383bde31145c9a7 to your computer and use it in GitHub Desktop.
Single-file zero-dependency nodejs web-based TCP client
const http = require('http');
const tls = require('tls')
const net = require('net');
function setupTls({host, port}) {
return new Promise((resolve) => {
const socket = tls.connect(
{
port: port,
host: host,
servername: host, // for SNI
},
() => {
resolve(socket)
}
)
});
}
function setupPlain({host, port}) {
return new Promise(resolve => {
const socket = new net.Socket();
socket.on('connect', () => {
resolve(socket);
});
socket.connect(port, host);
});
}
function exchange({dataCallback, endCallback, isTls=false, body, ...rest}) {
(isTls ? setupTls : setupPlain)({...rest, port: rest.port || (isTls?443:80)}).then(socket => {
socket.on('close', endCallback)
socket.on('data', dataCallback)
socket.on('error', (error) => {
console.log('Error', error)
})
socket.write(body) // send the HTTP request
});
}
const pageContent = `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Raw HTTP client</title>
<style>
textarea {
display: block;
width: 100%;
height: 20em;
}
input[type='text'] {
width: 100%;
display: block;
}
</style>
</head>
<body>
<h2>Raw HTTP client</h2>
<p>Host</p>
<input type="text" id="host" value="" />
<p>Request</p>
<textarea id="request"></textarea>
<button type="button" onclick="send()">Send</button>
<p>Response</p>
<textarea id="response" disabled></textarea>
<script>
function send() {
const host = document.querySelector("#host");
const input = document.querySelector("#request");
const output = document.querySelector("#response");
const hostPort = host.value.split(":");
fetch("/api", {
method: "POST",
body: JSON.stringify({
host: hostPort[0],
port: (hostPort.length > 1) ? parseInt(hostPort[1]) : false,
body: input.value,
}),
}).then(async resp => {
output.value = "";
const reader = resp.body.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const {value, done} = await reader.read();
if (done) break;
output.value += value;
}
});
}
</script>
</body>
<html>
`;
const requestListener = function (req, res) {
console.log(req.url);
if (req.url === "/api") {
let requestData = '';
req.on('data', chunk => requestData += chunk);
req.on('end', () => {
const requestInfo = JSON.parse(requestData);
res.writeHead(200, 'OK', {
"Content-Type": "text/plain",
});
exchange({
...requestInfo,
dataCallback: data => {
res.write(data);
console.log(".")
},
endCallback: res.end,
})
});
} else {
res.writeHead(200, 'OK', {
"Content-Type": "text/html",
});
res.end(pageContent);
}
}
const server = http.createServer(requestListener);
const port = 8084;
server.listen(port);
console.log(`listening on ${port}`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment