-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
The attach add-on sometimes reorders data despite the frames being received in-order over the socket.
Details
- Browser and browser version: Chrome 71.0.3578.98
- OS version: OSX 10.13.4
- xterm.js version: 3.10.1 (not positive, but don't think the plugin has changed since)
Steps to reproduce
- Attach the terminal to a websocket.
- Send the websocket two consecutive messages where the first's .data will be a Blob and the second an ArrayBuffer or string.
Expected: The data from these two messages to be displayed in order.
Actual: They're sometimes reordered.
My hack fix
The issue appears to be that FileReader's readAsArrayBuffer as asynchronous, so subsequent messages can be received/decoded/displayed while the FileReader is still loading.
I'm able to work around this by attaching an ID to each displayable message as they come in, and using that in displayData to check whether there are outstanding messages (and buffering them if there are). I'm patching the JS, not the TS but hopefully the idea comes across:
var nextId = 1;
var lastDisplayedId = 0;
var msgsToWrite = {};
var myTextDecoder;
addonTerminal.__getMessage = function (ev) {
var str;
if (typeof ev.data === 'object') {
var msgId = nextId++;
if (!myTextDecoder) {
myTextDecoder = new TextDecoder();
}
if (ev.data instanceof ArrayBuffer) {
str = myTextDecoder.decode(ev.data);
displayData(msgId, str);
}
else {
var fileReader_1 = new FileReader();
fileReader_1.addEventListener('load', function () {
str = myTextDecoder.decode(fileReader_1.result);
displayData(msgId, str);
});
fileReader_1.readAsArrayBuffer(ev.data);
}
}
else if (typeof ev.data === 'string') {
displayData(nextId++, ev.data);
}
else {
throw Error("Cannot handle \"" + typeof ev.data + "\" websocket message.");
}
};
function displayData(msgId, str, data) {
msgsToWrite[msgId] = (str || data);
for (var i = lastDisplayedId + 1; typeof msgsToWrite[i] !== 'undefined'; i++) {
if (buffered) {
addonTerminal.__pushToBuffer(msgsToWrite[i]);
}
else {
addonTerminal.write(msgsToWrite[i]);
}
delete msgsToWrite[i];
lastDisplayedId = i;
}
}
This seems to be working great, the only concern I have is recoverability if the filereader fails to decode a chunk--with this change it'll buffer subsequent data till the end of time.
Thanks for your work on this fantastic software.