comparison webtty.js @ 86:ad4229cf8321

WebTTY: use the pty.js module. Convert TermSessions to spawn with the pty.js module instead of piping everything through ptyhelper.
author John "Elwin" Edwards <elwin@sdf.org>
date Sun, 08 Jul 2012 21:20:56 -0700
parents e4773ac5d4d5
children f14e92f6d955
comparison
equal deleted inserted replaced
85:4303d94d87a2 86:ad4229cf8321
1 #!/usr/bin/env node 1 #!/usr/bin/env node
2 var http = require('http'); 2 var http = require('http');
3 var url = require('url'); 3 var url = require('url');
4 var path = require('path'); 4 var path = require('path');
5 var fs = require('fs'); 5 var fs = require('fs');
6 var child_process = require("child_process"); 6 var pty = require("/usr/local/lib/node_modules/pty.js");
7 7
8 var serveStaticRoot = "/home/elwin/hk/nodejs/rlg/s/"; 8 var serveStaticRoot = "/home/elwin/hk/nodejs/rlg/s/";
9 var ptyhelp = "/home/elwin/hk/nodejs/rlg/ptyhelper"; 9 var ptyhelp = "/home/elwin/hk/nodejs/rlg/ptyhelper";
10 var sessions = {}; 10 var sessions = {};
11 11
28 var childenv = {}; 28 var childenv = {};
29 for (var key in process.env) { 29 for (var key in process.env) {
30 if (!(key in env_dontuse)) 30 if (!(key in env_dontuse))
31 childenv[key] = process.env[key]; 31 childenv[key] = process.env[key];
32 } 32 }
33 childenv["PTYHELPER"] = String(this.h) + "x" + String(this.w); 33 var spawnopts = {"env": childenv, "cwd": process.env["HOME"],
34 // Should setsid get set? 34 "rows": this.h, "cols": this.w};
35 var spawnopts = {"env": childenv, "cwd": process.env["HOME"]}; 35 this.term = pty.spawn("bash", [], spawnopts);
36 this.child = child_process.spawn(ptyhelp, ["bash"], spawnopts);
37 var ss = this; 36 var ss = this;
38 /* Eventually we'll need to make sure the sessid isn't in use yet. */ 37 /* Eventually we'll need to make sure the sessid isn't in use yet. */
39 this.sessid = sessid; 38 this.sessid = sessid;
40 this.alive = true; 39 this.alive = true;
41 this.data = []; // Buffer for the process' output. 40 this.data = []; // Buffer for the process' output.
42 this.nsend = 0; // Number to use for the next message sent. 41 this.nsend = 0; // Number to use for the next message sent.
43 this.nrecv = 0; // Number expected on the next message received. 42 this.nrecv = 0; // Number expected on the next message received.
44 this.msgQ = []; // Queue for messages that arrived out of order. 43 this.msgQ = []; // Queue for messages that arrived out of order.
45 this.child.stdout.on("data", function (buf) { 44 this.term.on("data", function (buf) {
46 ss.data.push(buf); 45 ss.data.push(buf);
47 }); 46 });
48 this.child.stderr.on("data", function (buf) { 47 this.term.on("exit", function () {
49 ss.data.push(buf);
50 });
51 this.child.on("exit", function (code, signal) {
52 ss.exitcode = (code != null ? code : 255);
53 ss.alive = false; 48 ss.alive = false;
54 /* Wait for all the data to get collected */ 49 /* Wait for all the data to get collected */
55 setTimeout(ss.cleanup, 1000); 50 setTimeout(ss.cleanup, 1000);
56 }); 51 });
57 this.write = function (data, n) { 52 this.write = function (data, n) {
61 } 56 }
62 if (n !== this.nrecv) { 57 if (n !== this.nrecv) {
63 console.log("Session " + this.sessid + ": Expected message " + this.nrecv + ", got " + n); 58 console.log("Session " + this.sessid + ": Expected message " + this.nrecv + ", got " + n);
64 } 59 }
65 this.nrecv = n + 1; 60 this.nrecv = n + 1;
66 this.child.stdin.write(data); 61 this.term.write(data);
67 }; 62 };
68 this.read = function () { 63 this.read = function () {
69 if (this.data.length == 0) 64 if (this.data.length == 0)
70 return null; 65 return null;
71 var pos = 0; 66 var pos = 0;
72 var i = 0; 67 var i = 0;
73 for (i = 0; i < this.data.length; i++) 68 for (i = 0; i < this.data.length; i++)
74 pos += this.data[i].length; 69 pos += Buffer.byteLength(this.data[i]);
75 var nbuf = new Buffer(pos); 70 var nbuf = new Buffer(pos);
76 var tptr; 71 var tptr;
77 pos = 0; 72 pos = 0;
78 while (this.data.length > 0) { 73 while (this.data.length > 0) {
79 tptr = this.data.shift(); 74 tptr = new Buffer(this.data.shift());
80 tptr.copy(nbuf, pos); 75 tptr.copy(nbuf, pos);
81 pos += tptr.length; 76 pos += tptr.length;
82 } 77 }
83 return nbuf; 78 return nbuf;
84 }; 79 };
85 this.close = function () { 80 this.close = function () {
86 if (this.alive) 81 if (this.alive)
87 this.child.kill('SIGHUP'); 82 this.term.kill('SIGHUP');
88 }; 83 };
89 this.cleanup = function () { 84 this.cleanup = function () {
90 /* Call this when the child is dead. */ 85 /* Call this when the child is dead. */
91 if (this.alive) 86 if (this.alive)
92 return; 87 return;
184 resheaders["Set-Cookie"] = "ID=" + sessid; 179 resheaders["Set-Cookie"] = "ID=" + sessid;
185 res.writeHead(200, resheaders); 180 res.writeHead(200, resheaders);
186 var logindict = {"login": true, "id": sessid, "w": w, "h": h}; 181 var logindict = {"login": true, "id": sessid, "w": w, "h": h};
187 res.write(JSON.stringify(logindict)); 182 res.write(JSON.stringify(logindict));
188 res.end(); 183 res.end();
189 console.log("Started new session with key " + sessid + ", pid " + nsession.child.pid); 184 console.log("Started new session with key " + sessid + ", pid " + nsession.term.pid);
190 return; 185 return;
191 } 186 }
192 187
193 function findTermSession(req) { 188 function findTermSession(req) {
194 var cookies = getCookies(req); 189 var cookies = getCookies(req);
351 } 346 }
352 347
353 process.on("exit", function () { 348 process.on("exit", function () {
354 for (var sessid in sessions) { 349 for (var sessid in sessions) {
355 if (sessions[sessid].alive) 350 if (sessions[sessid].alive)
356 sessions[sessid].child.kill('SIGHUP'); 351 sessions[sessid].term.kill('SIGHUP');
357 } 352 }
358 console.log("Quitting..."); 353 console.log("Quitting...");
359 return; 354 return;
360 }); 355 });
361 356