UNPKG

@twilio/plugin-microvisor

Version:

Interact with your Twilio Microvisor devices

123 lines (122 loc) 5.5 kB
"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;