Skip to content

Commit add8035

Browse files
authored
Merge #404 show contents of invalid RPC message
2 parents 65ddb5e + 59136a4 commit add8035

File tree

7 files changed

+69
-7
lines changed

7 files changed

+69
-7
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

packages/neovim/src/attach.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/neovim/src/host/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { attach } from '../attach';
1+
import { attach } from '../attach/attach';
22
import { loadPlugin, LoadPluginOptions } from './factory';
33
import { NvimPlugin } from './NvimPlugin';
44

packages/neovim/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { attach } from './attach';
1+
export { attach } from './attach/attach';
22
export { Neovim, NeovimClient, Buffer, Tabpage, Window } from './api/index';
33
export { Plugin, Function, Autocmd, Command } from './plugin';
44
export { NvimPlugin } from './host/NvimPlugin';

packages/neovim/src/testUtil.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as jest from '@jest/globals';
44
import * as fs from 'node:fs';
55
import * as path from 'node:path';
66
import { NeovimClient } from './api/client';
7-
import { attach } from './attach';
7+
import { attach } from './attach/attach';
88
import { findNvim } from './utils/findNvim';
99
import { getLogger } from './utils/logger';
1010

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: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import { EventEmitter } from 'node:events';
6+
import { inspect } from 'node:util';
67

78
import {
89
encode,
@@ -12,6 +13,14 @@ import {
1213
} from '@msgpack/msgpack';
1314
import { Metadata } from '../api/types';
1415

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+
1524
class Response {
1625
private requestId: number;
1726

@@ -111,12 +120,35 @@ class Transport extends EventEmitter {
111120
iter.next().then(resolved => {
112121
if (!resolved.done) {
113122
if (!Array.isArray(resolved.value)) {
114-
throw new TypeError('expected msgpack result to be array');
123+
let valstr = '?';
124+
try {
125+
valstr = inspect(resolved.value, {
126+
sorted: true,
127+
maxArrayLength: 10,
128+
maxStringLength: 500,
129+
compact: true,
130+
breakLength: 500,
131+
});
132+
} catch (error) {
133+
// Do nothing.
134+
}
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);
115145
}
146+
116147
this.parseMessage(resolved.value);
117-
return resolveGeneratorRecursively(iter);
148+
resolveGeneratorRecursively(iter);
149+
return;
118150
}
119-
return Promise.resolve();
151+
Promise.resolve();
120152
});
121153
};
122154

0 commit comments

Comments
 (0)