Fix web inspector: endianness (LE), ECONNRESET, persistent discovery
- protocol.mjs: all reads/writes switched to LE to match serial.h - node_client.mjs: persistent error handler prevents ECONNRESET crash - discovery.mjs: remove unnecessary SO_REUSEPORT - server.mjs: discovery runs at startup (not per SSE open); uses EventEmitter + known_peers Map so SSE replays existing peers on connect Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -33,9 +33,11 @@ export class Node_Client extends EventEmitter {
|
||||
this.port = port;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let connected = false;
|
||||
const sock = net.createConnection({ host, port });
|
||||
|
||||
sock.once('connect', () => {
|
||||
connected = true;
|
||||
this._socket = sock;
|
||||
this.connected = true;
|
||||
this._buf = Buffer.alloc(0);
|
||||
@@ -43,9 +45,11 @@ export class Node_Client extends EventEmitter {
|
||||
resolve();
|
||||
});
|
||||
|
||||
sock.once('error', err => {
|
||||
if (!this.connected) { reject(err); }
|
||||
else { this.emit('error', err); }
|
||||
/* Single persistent error handler — errors after connect are
|
||||
* non-fatal here; the 'close' event fires next and handles cleanup. */
|
||||
sock.on('error', err => {
|
||||
if (!connected) { reject(err); }
|
||||
else { console.error('video-node socket error:', err.message); }
|
||||
});
|
||||
|
||||
sock.on('data', chunk => this._on_data(chunk));
|
||||
@@ -53,7 +57,6 @@ export class Node_Client extends EventEmitter {
|
||||
sock.on('close', () => {
|
||||
this.connected = false;
|
||||
this._socket = null;
|
||||
/* reject all pending requests */
|
||||
for (const { reject: rej } of this._pending.values()) {
|
||||
rej(new Error('disconnected'));
|
||||
}
|
||||
@@ -73,8 +76,8 @@ export class Node_Client extends EventEmitter {
|
||||
this._buf = Buffer.concat([this._buf, chunk]);
|
||||
while (true) {
|
||||
if (this._buf.length < FRAME_HEADER_SIZE) { break; }
|
||||
const msg_type = this._buf.readUInt16BE(0);
|
||||
const payload_len = this._buf.readUInt32BE(2);
|
||||
const msg_type = this._buf.readUInt16LE(0);
|
||||
const payload_len = this._buf.readUInt32LE(2);
|
||||
if (this._buf.length < FRAME_HEADER_SIZE + payload_len) { break; }
|
||||
const payload = this._buf.slice(FRAME_HEADER_SIZE, FRAME_HEADER_SIZE + payload_len);
|
||||
this._buf = this._buf.slice(FRAME_HEADER_SIZE + payload_len);
|
||||
@@ -85,7 +88,7 @@ export class Node_Client extends EventEmitter {
|
||||
_on_frame(msg_type, payload) {
|
||||
if (msg_type !== MSG_CONTROL_RESPONSE) { return; }
|
||||
if (payload.length < 4) { return; }
|
||||
const request_id = payload.readUInt16BE(0);
|
||||
const request_id = payload.readUInt16LE(0);
|
||||
const entry = this._pending.get(request_id);
|
||||
if (!entry) { return; }
|
||||
this._pending.delete(request_id);
|
||||
@@ -100,7 +103,7 @@ export class Node_Client extends EventEmitter {
|
||||
if (!this.connected) { return Promise.reject(new Error('not connected')); }
|
||||
const id = this._alloc_id();
|
||||
/* patch request_id into the frame payload (bytes 6-7) */
|
||||
frame.writeUInt16BE(id, FRAME_HEADER_SIZE);
|
||||
frame.writeUInt16LE(id, FRAME_HEADER_SIZE);
|
||||
return new Promise((resolve, reject) => {
|
||||
this._pending.set(id, { resolve, reject, decode: decode_fn });
|
||||
this._socket.write(frame, err => {
|
||||
|
||||
Reference in New Issue
Block a user