Mercurial > hg > rlgwebd
view shterm.js @ 165:59e62710cbb5
rlgwebd.js: prevent races when reading ttyrecs.
DglSession objects read a 12-byte TTYREC header, extract therefrom the
length of the data chunk, and then read the data. In between these two
reads, the file watcher could trigger another readchunk() invocation,
which might attempt to read a header from the beginning of the data
chunk. This usually results in expecting a data chunk of several GB
and failing to create a Buffer for it.
The race is remedied by setting a flag on the DglSession object
whenever readchunk() is called, clearing it when both reads complete,
and refusing to read if it is already set.
author | John "Elwin" Edwards |
---|---|
date | Wed, 07 Jan 2015 13:18:35 -0500 |
parents | c4a32007d2dc |
children |
line wrap: on
line source
/* shterm.js: browser-side JavaScript to handle I/O for termemu.js when it * is running a shell via the webtty.js server. */ var isalive = false; // Whether the session is currently active. var conn = null; // WebSocket function writeData(hexstr) { var codenum; var codes = []; var nc; var u8wait = 0; /* Stores bits from previous bytes of multibyte sequences. */ var expect = 0; /* The number of 10------ bytes expected. */ /* UTF-8 translation. */ for (var i = 0; i < hexstr.length; i += 2) { nc = Number("0x" + hexstr.substr(i, 2)); if (nc < 0x7F) { /* 0------- */ codes.push(nc); /* Any incomplete sequence will be discarded. */ u8wait = 0; expect = 0; } else if (nc < 0xC0) { /* 10------ : part of a multibyte sequence */ if (expect > 0) { u8wait <<= 6; u8wait += (nc & 0x3F); expect--; if (expect == 0) { codes.push(u8wait); u8wait = 0; } } else { /* Assume an initial byte was missed. */ u8wait = 0; } } /* These will all discard any incomplete sequence. */ else if (nc < 0xE0) { /* 110----- : introduces 2-byte sequence */ u8wait = (nc & 0x1F); expect = 1; } else if (nc < 0xF0) { /* 1110---- : introduces 3-byte sequence */ u8wait = (nc & 0x0F); expect = 2; } else if (nc < 0xF8) { /* 11110--- : introduces 4-byte sequence */ u8wait = (nc & 0x07); expect = 3; } else if (nc < 0xFC) { /* 111110-- : introduces 5-byte sequence */ u8wait = (nc & 0x03); expect = 4; } else if (nc < 0xFE) { /* 1111110- : introduces 6-byte sequence */ u8wait = (nc & 0x01); expect = 5; } else { /* 1111111- : should never appear */ u8wait = 0; expect = 0; } /* Supporting all 31 bits is probably overkill... */ } termemu.write(codes); return; } function sendback(str) { /* For responding to terminal queries. */ if (conn) { var msgObj = {"t": "d", "d": str}; conn.send(JSON.stringify(msgObj)); } else { debug(1, "No connection: cannot send " + str); } return; } function sendkey(ev) { if (!isalive) return; var keynum = ev.keyCode; var code; if (keynum >= 65 && keynum <= 90) { /* Letters. */ if (ev.ctrlKey) keynum -= 64; else if (!ev.shiftKey) keynum += 32; code = keynum.toString(16); if (code.length < 2) code = "0" + code; } else if (keynum >= 48 && keynum <= 57) { /* The number row. */ if (ev.shiftKey) { code = numShifts[keynum - 48].toString(16); } else { code = keynum.toString(16); } } else if (keynum in keyHexCodes) { if (ev.shiftKey) code = keyHexCodes[keynum][1]; else code = keyHexCodes[keynum][0]; } else if (keynum >= 16 && keynum <= 20) { return; } else { debug(1, "Ignoring keycode " + keynum); return; } ev.preventDefault(); var msgObj = {"t": "d", "d": code}; conn.send(JSON.stringify(msgObj)); //dkey(code); return; } var charshifts = { '-': "5f", '=': "2b", '[': "7b", ']': "7d", '\\': "7c", ';': "3a", '\'': "22", ',': "3c", '.': "3e", '/': "3f", '`': "7e" } var kpkeys = { "KP1": "1b4f46", "KP2": "1b4f42", "KP3": "1b5b367e", "KP4": "1b4f44", "KP5": "1b5b45", "KP6": "1b4f43", "KP7": "1b4f48", "KP8": "1b4f41", "KP9": "1b5b357e" }; function vkey(c) { if (!isalive) return; var keystr; if (c.match(/^[a-z]$/)) { if (termemu.ctrlp()) { var n = c.charCodeAt(0) - 96; keystr = n.toString(16); if (keystr.length < 2) keystr = "0" + keystr; } else if (termemu.shiftp()) keystr = c.toUpperCase().charCodeAt(0).toString(16); else keystr = c.charCodeAt(0).toString(16); } else if (c.match(/^[0-9]$/)) { if (termemu.shiftp()) keystr = numShifts[c.charCodeAt(0) - 48].toString(16); else keystr = c.charCodeAt(0).toString(16); } else if (c == '\n') keystr = "0a"; else if (c == '\t') keystr = "09"; else if (c == '\b') keystr = "08"; else if (c == ' ') keystr = "20"; else if (c in charshifts) { if (termemu.shiftp()) keystr = charshifts[c]; else keystr = c.charCodeAt(0).toString(16); } else if (c in kpkeys) { keystr = kpkeys[c]; } else return; //writeData("Sending " + keystr); var msgObj = {"t": "d", "d": keystr}; conn.send(JSON.stringify(msgObj)); return; } function setup() { keyHexCodes.init(); termemu.init("termwrap", 24, 80); setTitle("Not connected."); return; } function toggleshift() { termemu.toggleshift(); keydiv = document.getElementById("shiftkey"); if (termemu.shiftp()) keydiv.className = "keysel"; else keydiv.className = "key"; return; } function togglectrl() { termemu.togglectrl(); keydiv = document.getElementById("ctrlkey"); if (termemu.ctrlp()) keydiv.className = "keysel"; else keydiv.className = "key"; return; } function login(h, w) { if (conn) return; if (!(window.WebSocket)) { debug(1, "Cannot connect: WebSockets not supported"); return; } var sockurl = "ws://" + window.location.host + "/sock?w=" + w + "&h=" + h; conn = new WebSocket(sockurl); conn.onopen = function (event) { isalive = true; setTitle("Logged in"); debug(1, "Logged in via WebSocket"); } conn.onmessage = function (event) { var msgObj = JSON.parse(event.data); if (msgObj.t == 'l') { termemu.resize(msgObj.h, msgObj.w); } else if (msgObj.t == 'd') { debug(0, msgObj.d); writeData(msgObj.d); } else if (msgObj.t == 'q') { debug(0, "Quit message!"); conn.close(); } } conn.onclose = function (event) { conn = null; isalive = false; debug(1, "WebSocket connection closed."); setTitle("Not connected."); } } function stop() { if (conn) { conn.close(); } else { debug(1, "Cannot stop: connection already closed"); } return; } function setTitle(tstr) { var titlespan = document.getElementById("ttitle"); var tnode = document.createTextNode(tstr); if (titlespan.childNodes.length == 0) titlespan.appendChild(tnode); else titlespan.replaceChild(tnode, titlespan.childNodes[0]); return; } function debug(level, msg) { if (level < debugSuppress) return; var msgdiv = document.createElement("div"); var msgtext = document.createTextNode(msg); msgdiv.appendChild(msgtext); document.getElementById("debug").appendChild(msgdiv); return; } /* This should be a termemu method. */ function textsize(larger) { var cssSize = document.getElementById("term").style.fontSize; if (!cssSize) return; var match = cssSize.match(/\d*/); if (!match) return; var csize = Number(match[0]); var nsize; if (larger) { if (csize >= 48) nsize = 48; else if (csize >= 20) nsize = csize + 4; else if (csize >= 12) nsize = csize + 2; else if (csize >= 8) nsize = csize + 1; else nsize = 8; } else { if (csize <= 8) nsize = 8; else if (csize <= 12) nsize = csize - 1; else if (csize <= 20) nsize = csize - 2; else if (csize <= 48) nsize = csize - 4; else nsize = 48; } document.getElementById("term").style.fontSize = nsize.toString() + "px"; termemu.fixsize(); debug(1, "Changing font size to " + nsize.toString()); return; } function bell(on) { var imgnode = document.getElementById("bell"); if (on) { imgnode.style.visibility = "visible"; window.setTimeout(bell, 1500, false); } else imgnode.style.visibility = "hidden"; return; } function dkey(codestr) { var dstr = "Keystring: "; for (var i = 0; i < codestr.length; i += 2) { code = Number("0x" + codestr.substr(i, 2)); if (code < 32 || (code >= 127 && code < 160)) dstr += "\\x" + code.toString(16); else dstr += String.fromCharCode(code); } debug(1, dstr); return; }