123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- 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 = `
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>XRemoteView</title>
- </head>
- <body style="margin: 0px">
- <img
- src="/image"
- style="display: block; object-fit: contain; height: 100vh; width: 100%"
- ondblclick="fullscreen(event.target)">
- <script>
- function fullscreen(el) {
- if (document.fullscreenElement) {
- document.exitFullscreen();
- } else if (el.requestFullscreen) {
- el.requestFullscreen();
- }
- }
- </script>
- </body>
- </html>
- `;
-
- 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}`);
|