Mercurial > hg > rlgwebd
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'); |
