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'); |