Skip to content

Commit 59136a4

Browse files
committed
feat(transport): show contents of invalid RPC message
Problem: Exception does not give any hint about the contents of an invalid RPC message: "TypeError: expected msgpack result to be array" Solution: Show the payload contents in the error message. Example: "TypeError: expected msgpack array, got: …"
1 parent 9265f2f commit 59136a4

File tree

3 files changed

+54
-6
lines changed

3 files changed

+54
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
makes it more useful for GUIs where `$PATH` is often different than the user's
99
shell. #267
1010
- `findNvim` supports optional `paths` and `dirs` parameters.
11+
- Invalid RPC error now shows the contents of the invalid RPC message. #404
1112

1213
## [5.2.1](https://github.com/neovim/node-client/compare/v5.2.0...v5.2.1)
1314

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* eslint-env jest */
2+
import { EventEmitter } from 'node:events';
3+
import { Readable, Writable } from 'node:stream';
4+
import * as msgpack from '@msgpack/msgpack';
5+
import { attach } from '../attach/attach';
6+
import { exportsForTesting } from './transport';
7+
8+
describe('transport', () => {
9+
it('throws on invalid RPC message', done => {
10+
const invalidPayload = { bogus: 'nonsense' };
11+
const onTransportFail: EventEmitter = exportsForTesting.onTransportFail;
12+
onTransportFail.on('fail', (errMsg: string) => {
13+
expect(errMsg).toEqual(
14+
"invalid msgpack-RPC message: expected array, got: { bogus: 'nonsense' }"
15+
);
16+
done();
17+
});
18+
19+
// Create fake reader/writer and send a (broken) message.
20+
const fakeReader = new Readable({ read() {} });
21+
const fakeWriter = new Writable({ write() {} });
22+
23+
const nvim = attach({ reader: fakeReader, writer: fakeWriter });
24+
void nvim; // eslint-disable-line no-void
25+
26+
// Simulate an invalid message on the channel.
27+
const msg = msgpack.encode(invalidPayload);
28+
fakeReader.push(Buffer.from(msg.buffer, msg.byteOffset, msg.byteLength));
29+
});
30+
});

packages/neovim/src/utils/transport.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ import {
1313
} from '@msgpack/msgpack';
1414
import { Metadata } from '../api/types';
1515

16+
export let exportsForTesting: any; // eslint-disable-line import/no-mutable-exports
17+
// jest sets NODE_ENV=test.
18+
if (process.env.NODE_ENV === 'test') {
19+
exportsForTesting = {
20+
onTransportFail: new EventEmitter(),
21+
};
22+
}
23+
1624
class Response {
1725
private requestId: number;
1826

@@ -122,16 +130,25 @@ class Transport extends EventEmitter {
122130
breakLength: 500,
123131
});
124132
} catch (error) {
125-
console.error('Failed to inspect value: ', error);
133+
// Do nothing.
126134
}
127-
throw new TypeError(
128-
`Expected msgpack result to be an array, but got ${valstr}`
129-
);
135+
136+
const errMsg = `invalid msgpack-RPC message: expected array, got: ${valstr}`;
137+
const onFail: EventEmitter = exportsForTesting?.onTransportFail;
138+
if (onFail) {
139+
// HACK: for testing only.
140+
// TODO(justinmk): let the tests explicitly drive the messages.
141+
onFail.emit('fail', errMsg);
142+
return;
143+
}
144+
throw new TypeError(errMsg);
130145
}
146+
131147
this.parseMessage(resolved.value);
132-
return resolveGeneratorRecursively(iter);
148+
resolveGeneratorRecursively(iter);
149+
return;
133150
}
134-
return Promise.resolve();
151+
Promise.resolve();
135152
});
136153
};
137154

0 commit comments

Comments
 (0)