comparison webtty.js @ 140:789c094675f4

WebTTY: use WebSockets when possible.
author John "Elwin" Edwards
date Mon, 22 Jul 2013 07:51:53 -0700
parents f14e92f6d955
children c4a32007d2dc
comparison
equal deleted inserted replaced
139:dcd07c1d846a 140:789c094675f4
4 var http = require('http'); 4 var http = require('http');
5 var url = require('url'); 5 var url = require('url');
6 var path = require('path'); 6 var path = require('path');
7 var fs = require('fs'); 7 var fs = require('fs');
8 var pty = require(path.join(localModules, "pty.js")); 8 var pty = require(path.join(localModules, "pty.js"));
9 var child_process = require("child_process");
10 var webSocketServer = require(path.join(localModules, "websocket")).server;
9 11
10 var serveStaticRoot = fs.realpathSync("."); 12 var serveStaticRoot = fs.realpathSync(".");
11 var sessions = {}; 13 var sessions = {};
14 var sessionsWS = {};
12 15
13 var env_dontuse = {"TMUX": true, "TMUX_PANE": true}; 16 var env_dontuse = {"TMUX": true, "TMUX_PANE": true};
14 17
15 /* Constructor for TermSessions. Note that it opens the terminal and 18 /* Constructor for TermSessions. Note that it opens the terminal and
16 * adds itself to the sessions dict. 19 * adds itself to the sessions dict.
17 */ 20 */
21 function TermSessionWS(conn, h, w) {
22 var ss = this;
23 /* Set up the sizes. */
24 w = Math.floor(Number(w));
25 if (!(w > 0 && w < 256))
26 w = 80;
27 this.w = w;
28 h = Math.floor(Number(h));
29 if (!(h > 0 && h < 256))
30 h = 25;
31 this.h = h;
32 this.conn = conn;
33 /* Customize the environment. */
34 var childenv = {};
35 for (var key in process.env) {
36 if (!(key in env_dontuse))
37 childenv[key] = process.env[key];
38 }
39 var spawnopts = {"env": childenv, "cwd": process.env["HOME"],
40 "rows": this.h, "cols": this.w};
41 this.term = pty.spawn("bash", [], spawnopts);
42 this.alive = true;
43 this.term.on("data", function (datastr) {
44 var buf = new Buffer(datastr);
45 if (ss.conn.connected)
46 ss.conn.sendUTF(JSON.stringify({"t": "d", "d": buf.toString("hex")}));
47 });
48 this.term.on("exit", function () {
49 ss.alive = false;
50 /* Wait for all the data to get collected */
51 setTimeout(ss.cleanup, 1000);
52 });
53 this.conn.on("message", function (msg) {
54 try {
55 var msgObj = JSON.parse(msg.utf8Data);
56 }
57 catch (e) {
58 return;
59 }
60 if (msgObj.t == "d") {
61 var hexstr = msgObj["d"].replace(/[^0-9a-f]/gi, "");
62 if (hexstr.length % 2 != 0) {
63 return;
64 }
65 var keybuf = new Buffer(hexstr, "hex");
66 ss.term.write(keybuf);
67 }
68 });
69 this.conn.on("close", function (msg) {
70 if (ss.alive)
71 ss.term.kill('SIGHUP');
72 console.log("WebSocket connection closed.");
73 });
74 this.cleanup = function () {
75 /* Call this when the child is dead. */
76 if (ss.alive)
77 return;
78 if (ss.conn.connected) {
79 ss.conn.sendUTF(JSON.stringify({"t": "q"}));
80 }
81 };
82 this.conn.sendUTF(JSON.stringify({"t": "l", "w": w, "h": h}));
83 console.log("New WebSocket connection.");
84 }
85
18 function TermSession(sessid, h, w) { 86 function TermSession(sessid, h, w) {
19 /* Set up the sizes. */ 87 /* Set up the sizes. */
20 w = Math.floor(Number(w)); 88 w = Math.floor(Number(w));
21 if (!(w > 0 && w < 256)) 89 if (!(w > 0 && w < 256))
22 w = 80; 90 w = 80;
353 } 421 }
354 console.log("Quitting..."); 422 console.log("Quitting...");
355 return; 423 return;
356 }); 424 });
357 425
426 function wsRespond(req) {
427 var w, h, conn;
428 if (req.resourceURL.pathname == "/sock") {
429 w = parseInt(req.resourceURL.query.w);
430 if (isNaN(w) || w <= 0 || w > 256)
431 w = 80;
432 h = parseInt(req.resourceURL.query.h);
433 if (isNaN(h) || h <= 0 || h > 256)
434 h = 25;
435 conn = req.accept(null, req.origin);
436 new TermSessionWS(conn, h, w);
437 }
438 else {
439 req.reject(404, "No such resource.");
440 }
441 }
442
443 /* The pty.js module doesn't wait for the processes it spawns, so they
444 * become zombies, which leads to unpleasantness when the system runs
445 * out of process table entries. But if the child_process module is
446 * initialized and a child spawned, node will continue waiting for any
447 * children.
448 * Someday, some developer will get the bright idea of tracking how many
449 * processes the child_process module has spawned, and not waiting if
450 * it's zero. Until then, the following useless line will protect us
451 * from the zombie hordes.
452 * Figuring this out was almost as interesting as the Rogue bug where
453 * printf debugging altered whether the high score list was checked.
454 */
455 child_process.spawn("/bin/true");
456
358 process.env["TERM"] = "xterm-256color"; 457 process.env["TERM"] = "xterm-256color";
359 http.createServer(handler).listen(8080, "127.0.0.1"); 458 var webServer = http.createServer(handler);
459 webServer.listen(8080, "127.0.0.1");
360 console.log('Server running at http://127.0.0.1:8080/'); 460 console.log('Server running at http://127.0.0.1:8080/');
461 var wsServer = new webSocketServer({"httpServer": webServer});
462 wsServer.on("request", wsRespond);
463 console.log('WebSockets online');