You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

server.js 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. let net = require("net");
  2. let fs = require("fs");
  3. let tcpport = process.env["TCPPORT"] ? parseInt(process.env["TCPPORT"]) : 8099;
  4. let httpport = process.env["HTTPPORT"] ? parseInt(process.env["HTTPPORT"]) : 8098;
  5. let html = `
  6. <!DOCTYPE html>
  7. <html>
  8. <head>
  9. <meta charset="utf-8">
  10. <title>XRemoteView</title>
  11. </head>
  12. <body style="margin: 0px">
  13. <img
  14. src="/image"
  15. style="display: block; object-fit: contain; height: 100vh; width: 100%"
  16. ondblclick="fullscreen(event.target)">
  17. <script>
  18. function fullscreen(el) {
  19. if (document.fullscreenElement) {
  20. document.exitFullscreen();
  21. } else if (el.requestFullscreen) {
  22. el.requestFullscreen();
  23. }
  24. }
  25. </script>
  26. </body>
  27. </html>
  28. `;
  29. let boundary = "mjpeg-boundary";
  30. let imgdata = fs.readFileSync("placeholder.jpg");
  31. let waiters = [];
  32. class Reader {
  33. constructor(stream) {
  34. this.stream = stream;
  35. this.buf = Buffer.alloc(0);
  36. this.resolve = null;
  37. this.resolveMax = null;
  38. this.good = true;
  39. this.stream.on("close", () => {
  40. this.good = false;
  41. if (this.resolve) {
  42. let resolve = this.resolve;
  43. this.resolve = null;
  44. resolve(Buffer.alloc(0));
  45. }
  46. });
  47. this.stream.on("error", err => {
  48. console.log(this.stream.remoteAddress + ": " + err.code);
  49. });
  50. this.stream.on("data", data => {
  51. let resolve = this.resolve;
  52. let max = this.resolveMax;
  53. if (resolve && data.length <= this.resolveMax) {
  54. this.resolve = null;
  55. resolve(data);
  56. } else if (this.resolve) {
  57. this.resolve = null;
  58. this.buf = data.slice(max);
  59. resolve(data.slice(0, max));
  60. } else {
  61. this.buf = Buffer.concat([this.buf, data]);
  62. }
  63. });
  64. }
  65. read(max) {
  66. if (this.buf.length > max) {
  67. let buf = this.buf.slice(0, max);
  68. this.buf = this.buf.slice(max);
  69. return Promise.resolve(buf);
  70. } else if (this.buf.length != 0) {
  71. let buf = this.buf;
  72. this.buf = Buffer.alloc(0);
  73. return buf;
  74. } else if (this.good) {
  75. this.resolveMax = max;
  76. return new Promise((resolve, reject) => {
  77. this.resolve = resolve;
  78. });
  79. } else {
  80. return Buffer.alloc(0);
  81. }
  82. }
  83. async readAll(length) {
  84. if (!this.good) {
  85. return Buffer.alloc(0);
  86. }
  87. let buf = Buffer.alloc(0);
  88. while (buf.length < length && this.good) {
  89. let b = await this.read(length - buf.length);
  90. buf = Buffer.concat([buf, b]);
  91. }
  92. return buf;
  93. }
  94. };
  95. async function respondToHttp(reader, conn) {
  96. let path = "";
  97. while (true) {
  98. let ch = (await reader.readAll(1)).toString();
  99. if (ch == " ") {
  100. break;
  101. }
  102. path += ch;
  103. }
  104. // We frankly don't care about anything else the client has to say
  105. if (path == "/image") {
  106. conn.write(
  107. "HTTP/1.1 200 OK\r\n" +
  108. "Content-Type: multipart/x-mixed-replace;boundary=" + boundary + "\r\n" +
  109. "\r\n");
  110. conn.write(
  111. "--" + boundary + "\r\n" +
  112. "Content-Type: image/jpeg\r\n" +
  113. "\r\n");
  114. conn.write(imgdata);
  115. conn.write(
  116. "\r\n" +
  117. "--" + boundary + "\r\n");
  118. let idx = waiters.length;
  119. waiters.push({ w: conn, fresh: true });
  120. conn.on("close", () => {
  121. delete waiters[idx];
  122. });
  123. } else {
  124. conn.write(
  125. "HTTP/1.1 200 OK\r\n" +
  126. "Connection: close\r\n" +
  127. "Content-Type: text/html\r\n" +
  128. "\r\n");
  129. conn.write(html);
  130. conn.end();
  131. }
  132. }
  133. net.createServer(async conn => {
  134. console.log("Connection", conn.remoteAddress);
  135. let reader = new Reader(conn);
  136. let magic = await reader.readAll(4);
  137. if (magic.equals(Buffer.from("GET "))) {
  138. return respondToHttp(reader, conn);
  139. }
  140. let length = magic.readUInt32BE(0);
  141. let received = Buffer.alloc(0);
  142. while (reader.good) {
  143. for (let waiter of waiters) {
  144. if (waiter == null) continue;
  145. waiter.fresh = false;
  146. waiter.w.write(
  147. "Content-Type: image/jpeg\r\n" +
  148. "\r\n");
  149. }
  150. while (length > 0) {
  151. let buf = await reader.read(length);
  152. if (buf.length == 0) {
  153. console.log(conn.remoteAddress, "disconnected.");
  154. return;
  155. }
  156. for (let waiter of waiters) {
  157. if (waiter && !waiter.fresh) {
  158. waiter.w.write(buf);
  159. }
  160. }
  161. received = Buffer.concat([received, buf]);
  162. length -= buf.length;
  163. }
  164. for (let waiter of waiters) {
  165. if (waiter && !waiter.fresh) {
  166. waiter.w.write(
  167. "\r\n" +
  168. "--" + boundary + "\r\n");
  169. }
  170. }
  171. imgdata = received;
  172. received = await reader.readAll(4);
  173. if (received.length < 4) {
  174. console.log(conn.remoteAddress, "disconnected.");
  175. return;
  176. }
  177. length = received.readUInt32BE(0);
  178. received = Buffer.alloc(0);
  179. }
  180. }).on("error", err => console.error(err)).listen(tcpport, "0.0.0.0");
  181. console.log(`TCP listening on 0.0.0.0:${tcpport}`);