@twilio/plugin-microvisor
Version:
Interact with your Twilio Microvisor devices
123 lines (122 loc) • 5.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GdbServer = void 0;
const node_net_1 = require("node:net");
const gdb2protobuf_1 = require("./gdb2protobuf");
const node_util_1 = require("node:util");
const node_events_1 = require("node:events");
var GdbControlType;
(function (GdbControlType) {
GdbControlType[GdbControlType["Ack"] = '+'.charCodeAt(0)] = "Ack";
GdbControlType[GdbControlType["NAck"] = '-'.charCodeAt(0)] = "NAck";
GdbControlType[GdbControlType["Data"] = '$'.charCodeAt(0)] = "Data";
GdbControlType[GdbControlType["Interrupt"] = 3] = "Interrupt";
GdbControlType[GdbControlType["Unknown"] = 255] = "Unknown";
})(GdbControlType || (GdbControlType = {}));
class GdbServer extends node_events_1.EventEmitter {
constructor(port, gdbToProtobuf) {
super();
this.server = undefined;
this.ackDisabled = false;
this.buffer = Buffer.concat([]);
this.waitingForResponse = false;
this.gdbToProtobuf = gdbToProtobuf;
this.gdbToProtobuf.on('open', () => {
if (typeof this.server !== 'undefined') {
return;
}
this.server = (0, node_net_1.createServer)(async (socket) => {
this.emit('info', `Accepted connection from ${socket.remoteAddress}:${socket.remotePort}`);
await gdbToProtobuf.hello();
socket.on('data', async (data) => this.onData(socket, data));
socket.on('close', async () => {
this.emit('info', `GDB connection from ${socket.remoteAddress}:${socket.remotePort} closed`);
await gdbToProtobuf.goodbye();
});
this.gdbToProtobuf.on('unsolicitedHalt', (hr) => {
this.emit('debug', `< ${hr.content}`);
socket.write(hr.content);
});
});
this.server.on('error', (error) => {
var _a;
this.emit('info', `GDB server error ${error.name}: '${error.message}'`);
(_a = this.server) === null || _a === void 0 ? void 0 : _a.close();
});
this.server.on('close', () => {
this.emit('info', 'GDB server shutdown');
this.server = undefined;
});
this.server.listen(port);
this.emit('info', `GDB server listening on port ${port}`);
});
this.gdbToProtobuf.on('close', () => {
if (typeof this.server !== 'undefined') {
this.server.close();
}
this.emit('close');
});
}
static messageIsBuffer(message) {
return message.buffer !== undefined;
}
async onData(socket, data) {
let dataBuf = (GdbServer.messageIsBuffer(data) ? data : Buffer.from(data));
this.buffer = Buffer.concat([this.buffer, dataBuf]);
if (this.waitingForResponse) {
return;
}
this.waitingForResponse = true;
outer: for (;;) {
if (this.buffer.byteLength === 0) {
break;
}
switch (this.buffer[0]) {
case GdbControlType.Ack:
case GdbControlType.NAck:
this.buffer = this.buffer.subarray(1);
// TODO: handle ack/nack
break;
case GdbControlType.Interrupt:
{
this.buffer = this.buffer.subarray(1);
this.emit('debug', '> ^C');
this.gdbToProtobuf.interrupt();
break;
}
case GdbControlType.Data:
{
let endidx = this.buffer.indexOf('#'.charCodeAt(0));
if (endidx === -1) {
break outer;
}
endidx += 3; // checksum
if (endidx > this.buffer.byteLength) {
break outer;
}
let packetContent = new node_util_1.TextDecoder().decode(this.buffer.subarray(0, endidx));
let packetBody = gdb2protobuf_1.GdbPacket.unescape(packetContent.slice(1, -3));
let cs = Number.parseInt(packetContent.slice(-2), 16);
if (cs === gdb2protobuf_1.GdbPacket.checksum(packetBody)) {
socket.write(String.fromCharCode(GdbControlType.Ack));
this.emit('debug', `> ${packetContent}`);
this.gdbToProtobuf.packet(new gdb2protobuf_1.GdbPacket(packetContent)).then((resp) => {
this.emit('debug', `< ${resp.content}`);
socket.write(resp.content);
});
}
else {
socket.write(String.fromCharCode(GdbControlType.NAck));
}
this.buffer = this.buffer.subarray(endidx);
break;
}
default:
this.emit('debug', `dropping ${this.buffer[0]} to resynchronise`);
this.buffer = this.buffer.subarray(1);
}
}
this.waitingForResponse = false;
}
}
exports.GdbServer = GdbServer;