v1 stdio buffer limit (#2239)
This commit is contained in:
parent
c36e1ef5bb
commit
e7ee57c2f3
3 changed files with 183 additions and 9 deletions
|
|
@ -75,3 +75,44 @@ test('should return child process pid', async () => {
|
|||
await client.close();
|
||||
expect(client.pid).toBeNull();
|
||||
});
|
||||
|
||||
test('should respect custom maxBufferSize option', async () => {
|
||||
const client = new StdioClientTransport({
|
||||
command: 'node',
|
||||
args: ['-e', 'process.stdout.write(Buffer.alloc(200, 0x41))'],
|
||||
maxBufferSize: 100
|
||||
});
|
||||
|
||||
const errorReceived = new Promise<Error>(resolve => {
|
||||
client.onerror = resolve;
|
||||
});
|
||||
const closed = new Promise<void>(resolve => {
|
||||
client.onclose = () => resolve();
|
||||
});
|
||||
|
||||
await client.start();
|
||||
|
||||
const error = await errorReceived;
|
||||
expect(error.message).toMatch(/ReadBuffer exceeded maximum size/);
|
||||
await closed;
|
||||
});
|
||||
|
||||
test('should fire onerror and close when ReadBuffer overflows', async () => {
|
||||
const client = new StdioClientTransport({
|
||||
command: 'node',
|
||||
args: ['-e', 'process.stdout.write(Buffer.alloc(11 * 1024 * 1024, 0x41))']
|
||||
});
|
||||
|
||||
const errorReceived = new Promise<Error>(resolve => {
|
||||
client.onerror = resolve;
|
||||
});
|
||||
const closed = new Promise<void>(resolve => {
|
||||
client.onclose = () => resolve();
|
||||
});
|
||||
|
||||
await client.start();
|
||||
|
||||
const error = await errorReceived;
|
||||
expect(error.message).toMatch(/ReadBuffer exceeded maximum size/);
|
||||
await closed;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -100,3 +100,51 @@ test('should read multiple messages', async () => {
|
|||
await finished;
|
||||
expect(readMessages).toEqual(messages);
|
||||
});
|
||||
|
||||
test('should respect custom maxBufferSize option', async () => {
|
||||
const server = new StdioServerTransport(input, output, { maxBufferSize: 100 });
|
||||
|
||||
let receivedError: Error | undefined;
|
||||
server.onerror = err => {
|
||||
receivedError = err;
|
||||
};
|
||||
let closeCount = 0;
|
||||
server.onclose = () => {
|
||||
closeCount++;
|
||||
};
|
||||
|
||||
await server.start();
|
||||
|
||||
// Push 101 bytes without a newline — exceeds the 100-byte limit
|
||||
input.push(Buffer.alloc(101, 0x41));
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
|
||||
expect(receivedError?.message).toMatch(/ReadBuffer exceeded maximum size/);
|
||||
expect(closeCount).toBe(1);
|
||||
});
|
||||
|
||||
test('should fire onerror and close when ReadBuffer overflows', async () => {
|
||||
const server = new StdioServerTransport(input, output);
|
||||
|
||||
let receivedError: Error | undefined;
|
||||
server.onerror = err => {
|
||||
receivedError = err;
|
||||
};
|
||||
let closeCount = 0;
|
||||
server.onclose = () => {
|
||||
closeCount++;
|
||||
};
|
||||
|
||||
await server.start();
|
||||
|
||||
// Push data exceeding the default 10 MB limit without a newline
|
||||
const chunk = Buffer.alloc(11 * 1024 * 1024, 0x41);
|
||||
input.push(chunk);
|
||||
|
||||
// Allow the close() promise to settle
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
|
||||
expect(receivedError?.message).toMatch(/ReadBuffer exceeded maximum size/);
|
||||
expect(closeCount).toBe(1);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { JSONRPCMessage } from '../../src/types.js';
|
||||
import { ReadBuffer } from '../../src/shared/stdio.js';
|
||||
import { STDIO_DEFAULT_MAX_BUFFER_SIZE, ReadBuffer } from '../../src/shared/stdio.js';
|
||||
|
||||
const testMessage: JSONRPCMessage = {
|
||||
jsonrpc: '2.0',
|
||||
|
|
@ -33,3 +33,46 @@ test('should be reusable after clearing', () => {
|
|||
readBuffer.append(Buffer.from('\n'));
|
||||
expect(readBuffer.readMessage()).toEqual(testMessage);
|
||||
});
|
||||
|
||||
describe('buffer size limit', () => {
|
||||
test('should throw when buffer exceeds default max size', () => {
|
||||
const readBuffer = new ReadBuffer();
|
||||
const chunkSize = 1024 * 1024; // 1 MB
|
||||
const chunk = Buffer.alloc(chunkSize);
|
||||
const chunksToFill = Math.floor(STDIO_DEFAULT_MAX_BUFFER_SIZE / chunkSize);
|
||||
for (let i = 0; i < chunksToFill; i++) {
|
||||
readBuffer.append(chunk);
|
||||
}
|
||||
expect(() => readBuffer.append(chunk)).toThrow(/ReadBuffer exceeded maximum size/);
|
||||
});
|
||||
|
||||
test('should throw when buffer exceeds custom max size', () => {
|
||||
const readBuffer = new ReadBuffer({ maxBufferSize: 100 });
|
||||
readBuffer.append(Buffer.alloc(50));
|
||||
expect(() => readBuffer.append(Buffer.alloc(51))).toThrow(/ReadBuffer exceeded maximum size/);
|
||||
});
|
||||
|
||||
test('should clear buffer before throwing on overflow', () => {
|
||||
const readBuffer = new ReadBuffer({ maxBufferSize: 100 });
|
||||
readBuffer.append(Buffer.alloc(50));
|
||||
expect(() => readBuffer.append(Buffer.alloc(51))).toThrow();
|
||||
|
||||
// Buffer should be cleared — can append again
|
||||
readBuffer.append(Buffer.alloc(50));
|
||||
// And read messages normally
|
||||
expect(readBuffer.readMessage()).toBeNull();
|
||||
});
|
||||
|
||||
test('should allow appending up to exactly the max size', () => {
|
||||
const readBuffer = new ReadBuffer({ maxBufferSize: 100 });
|
||||
// Should not throw — exactly at limit
|
||||
expect(() => readBuffer.append(Buffer.alloc(100))).not.toThrow();
|
||||
});
|
||||
|
||||
test('should work with no options (backwards compatible)', () => {
|
||||
const readBuffer = new ReadBuffer();
|
||||
// Small append should always work
|
||||
readBuffer.append(Buffer.from(JSON.stringify({ jsonrpc: '2.0', method: 'ping' }) + '\n'));
|
||||
expect(readBuffer.readMessage()).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue