let net = require("net");
let fs = require("fs");
let tcpport = process.env["TCPPORT"] ? parseInt(process.env["TCPPORT"]) : 8099;
let httpport = process.env["HTTPPORT"] ? parseInt(process.env["HTTPPORT"]) : 8098;
let html = `
XRemoteView
`;
let boundary = "mjpeg-boundary";
let imgdata = fs.readFileSync("placeholder.jpg");
let waiters = [];
class Reader {
constructor(stream) {
this.stream = stream;
this.buf = Buffer.alloc(0);
this.resolve = null;
this.resolveMax = null;
this.good = true;
this.stream.on("close", () => {
this.good = false;
if (this.resolve) {
let resolve = this.resolve;
this.resolve = null;
resolve(Buffer.alloc(0));
}
});
this.stream.on("error", err => {
console.log(this.stream.remoteAddress + ": " + err.code);
});
this.stream.on("data", data => {
let resolve = this.resolve;
let max = this.resolveMax;
if (resolve && data.length <= this.resolveMax) {
this.resolve = null;
resolve(data);
} else if (this.resolve) {
this.resolve = null;
this.buf = data.slice(max);
resolve(data.slice(0, max));
} else {
this.buf = Buffer.concat([this.buf, data]);
}
});
}
read(max) {
if (this.buf.length > max) {
let buf = this.buf.slice(0, max);
this.buf = this.buf.slice(max);
return Promise.resolve(buf);
} else if (this.buf.length != 0) {
let buf = this.buf;
this.buf = Buffer.alloc(0);
return buf;
} else if (this.good) {
this.resolveMax = max;
return new Promise((resolve, reject) => {
this.resolve = resolve;
});
} else {
return Buffer.alloc(0);
}
}
async readAll(length) {
if (!this.good) {
return Buffer.alloc(0);
}
let buf = Buffer.alloc(0);
while (buf.length < length && this.good) {
let b = await this.read(length - buf.length);
buf = Buffer.concat([buf, b]);
}
return buf;
}
};
async function respondToHttp(reader, conn) {
let path = "";
while (true) {
let ch = (await reader.readAll(1)).toString();
if (ch == " ") {
break;
}
path += ch;
}
// We frankly don't care about anything else the client has to say
if (path == "/image") {
conn.write(
"HTTP/1.1 200 OK\r\n" +
"Content-Type: multipart/x-mixed-replace;boundary=" + boundary + "\r\n" +
"\r\n");
conn.write(
"--" + boundary + "\r\n" +
"Content-Type: image/jpeg\r\n" +
"\r\n");
conn.write(imgdata);
conn.write(
"\r\n" +
"--" + boundary + "\r\n");
let idx = waiters.length;
waiters.push({ w: conn, fresh: true });
conn.on("close", () => {
delete waiters[idx];
});
} else {
conn.write(
"HTTP/1.1 200 OK\r\n" +
"Connection: close\r\n" +
"Content-Type: text/html\r\n" +
"\r\n");
conn.write(html);
conn.end();
}
}
net.createServer(async conn => {
console.log("Connection", conn.remoteAddress);
let reader = new Reader(conn);
let magic = await reader.readAll(4);
if (magic.equals(Buffer.from("GET "))) {
return respondToHttp(reader, conn);
}
let length = magic.readUInt32BE(0);
let received = Buffer.alloc(0);
while (reader.good) {
for (let waiter of waiters) {
if (waiter == null) continue;
waiter.fresh = false;
waiter.w.write(
"Content-Type: image/jpeg\r\n" +
"\r\n");
}
while (length > 0) {
let buf = await reader.read(length);
if (buf.length == 0) {
console.log(conn.remoteAddress, "disconnected.");
return;
}
for (let waiter of waiters) {
if (waiter && !waiter.fresh) {
waiter.w.write(buf);
}
}
received = Buffer.concat([received, buf]);
length -= buf.length;
}
for (let waiter of waiters) {
if (waiter && !waiter.fresh) {
waiter.w.write(
"\r\n" +
"--" + boundary + "\r\n");
}
}
imgdata = received;
received = await reader.readAll(4);
if (received.length < 4) {
console.log(conn.remoteAddress, "disconnected.");
return;
}
length = received.readUInt32BE(0);
received = Buffer.alloc(0);
}
}).on("error", err => console.error(err)).listen(tcpport, "0.0.0.0");
console.log(`TCP listening on 0.0.0.0:${tcpport}`);