diff --git a/src/common/parser/Constants.ts b/src/common/parser/Constants.ts index 9f28a27a..85156c3e 100644 --- a/src/common/parser/Constants.ts +++ b/src/common/parser/Constants.ts @@ -53,3 +53,6 @@ export const enum OscState { PAYLOAD = 2, ABORT = 3 } + +// payload limit for OSC and DCS +export const PAYLOAD_LIMIT = 10000000; diff --git a/src/common/parser/DcsParser.test.ts b/src/common/parser/DcsParser.test.ts index a48b99dd..172f51bc 100644 --- a/src/common/parser/DcsParser.test.ts +++ b/src/common/parser/DcsParser.test.ts @@ -7,6 +7,7 @@ import { DcsParser, DcsHandlerFactory } from 'common/parser/DcsParser'; import { IDcsHandler, IParams } from 'common/parser/Types'; import { utf32ToString, StringToUtf32 } from 'common/input/TextDecoder'; import { Params } from 'common/parser/Params'; +import { PAYLOAD_LIMIT } from 'common/parser/Constants'; function toUtf32(s: string): Uint32Array { const utf32 = new Uint32Array(s.length); @@ -176,5 +177,40 @@ describe('DcsParser', () => { parser.unhook(true); assert.deepEqual(reports, [['two', [1, 2, 3], 'Here comes the mouse!'], ['one', [1, 2, 3], 'some other data']]); }); + it('should respect return false', () => { + parser.setDcsHandler('+p', new DcsHandlerFactory((params, data) => reports.push(['one', params.toArray(), data]))); + parser.addDcsHandler('+p', new DcsHandlerFactory((params, data) => { reports.push(['two', params.toArray(), data]); return false; })); + parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0)); + let data = toUtf32('Here comes'); + parser.put(data, 0, data.length); + data = toUtf32(' the mouse!'); + parser.put(data, 0, data.length); + parser.unhook(true); + assert.deepEqual(reports, [['two', [1, 2, 3], 'Here comes the mouse!'], ['one', [1, 2, 3], 'Here comes the mouse!']]); + }); + it('should work up to payload limit', function(): void { + this.timeout(10000); + parser.setDcsHandler('+p', new DcsHandlerFactory((params, data) => reports.push([params.toArray(), data]))); + parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0)); + const data = toUtf32('A'.repeat(1000)); + for (let i = 0; i < PAYLOAD_LIMIT; i += 1000) { + parser.put(data, 0, data.length); + } + parser.unhook(true); + assert.deepEqual(reports, [[[1, 2, 3], 'A'.repeat(PAYLOAD_LIMIT)]]); + }); + it('should abort for payload limit +1', function(): void { + this.timeout(10000); + parser.setDcsHandler('+p', new DcsHandlerFactory((params, data) => reports.push([params.toArray(), data]))); + parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0)); + let data = toUtf32('A'.repeat(1000)); + for (let i = 0; i < PAYLOAD_LIMIT; i += 1000) { + parser.put(data, 0, data.length); + } + data = toUtf32('A'); + parser.put(data, 0, data.length); + parser.unhook(true); + assert.deepEqual(reports, []); + }); }); }); diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index d3f777dd..f89edcfc 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -6,7 +6,8 @@ import { IDisposable } from 'common/Types'; import { IDcsHandler, IParams, ParamsArray, IHandlerCollection, IDcsParser, DcsFallbackHandler } from 'common/parser/Types'; import { utf32ToString } from 'common/input/TextDecoder'; -import { Params } from './Params'; +import { Params } from 'common/parser/Params'; +import { PAYLOAD_LIMIT } from 'common/parser/Constants'; export class DcsParser implements IDcsParser { @@ -99,9 +100,6 @@ export class DcsParser implements IDcsParser { } } -// limit allowed payload for DcsHandlerFactory -const PAYLOAD_LIMIT = 50000000; - /** * Convenient class to create a DCS handler from a single callback function. * Note: The payload is currently limited to 50 MB (hardcoded). diff --git a/src/common/parser/OscParser.test.ts b/src/common/parser/OscParser.test.ts index 9b5de5fd..2d1180d2 100644 --- a/src/common/parser/OscParser.test.ts +++ b/src/common/parser/OscParser.test.ts @@ -6,6 +6,7 @@ import { assert } from 'chai'; import { OscParser, OscHandlerFactory } from 'common/parser/OscParser'; import { StringToUtf32, utf32ToString } from 'common/input/TextDecoder'; import { IOscHandler } from 'common/parser/Types'; +import { PAYLOAD_LIMIT } from 'common/parser/Constants'; function toUtf32(s: string): Uint32Array { const utf32 = new Uint32Array(s.length); @@ -218,5 +219,33 @@ describe('OscParser', () => { parser.end(true); assert.deepEqual(reports, [['two', 'Here comes the mouse!'], ['one', 'Here comes the mouse!']]); }); + it('should work up to payload limit', function(): void { + this.timeout(10000); + parser.setOscHandler(1234, new OscHandlerFactory(data => reports.push([1234, data]))); + parser.start(); + let data = toUtf32('1234;'); + parser.put(data, 0, data.length); + data = toUtf32('A'.repeat(1000)); + for (let i = 0; i < PAYLOAD_LIMIT; i += 1000) { + parser.put(data, 0, data.length); + } + parser.end(true); + assert.deepEqual(reports, [[1234, 'A'.repeat(PAYLOAD_LIMIT)]]); + }); + it('should abort for payload limit +1', function(): void { + this.timeout(10000); + parser.setOscHandler(1234, new OscHandlerFactory(data => reports.push([1234, data]))); + parser.start(); + let data = toUtf32('1234;'); + parser.put(data, 0, data.length); + data = toUtf32('A'.repeat(1000)); + for (let i = 0; i < PAYLOAD_LIMIT; i += 1000) { + parser.put(data, 0, data.length); + } + data = toUtf32('A'); + parser.put(data, 0, data.length); + parser.end(true); + assert.deepEqual(reports, []); + }); }); }); diff --git a/src/common/parser/OscParser.ts b/src/common/parser/OscParser.ts index f0c9c93c..cf363714 100644 --- a/src/common/parser/OscParser.ts +++ b/src/common/parser/OscParser.ts @@ -4,7 +4,7 @@ */ import { IOscHandler, IHandlerCollection, OscFallbackHandler } from 'common/parser/Types'; -import { OscState } from 'common/parser/Constants'; +import { OscState, PAYLOAD_LIMIT } from 'common/parser/Constants'; import { Disposable } from 'common/Lifecycle'; import { utf32ToString } from 'common/input/TextDecoder'; import { IDisposable } from 'common/Types'; @@ -162,10 +162,6 @@ export class OscParser extends Disposable { } } - -// limit allowed payload for OscHandlerFactory -const PAYLOAD_LIMIT = 50000000; - /** * Convenient class to allow attaching string based handler functions * as OSC handlers. diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index a0810309..806074a2 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -524,7 +524,7 @@ declare module 'xterm' { * There is currently no way to intercept smaller data chunks, those will be stored up * until the sequence is finished. Since DCS sequences are not limited by the amount * of data this might impose a problem for big payloads. Currently xterm.js limits - * DCS payload to 50 MB which should give enough room for most use cases. + * DCS payload to 10 MB which should give enough room for most use cases. * The function gets numerical parameter and the data as arguments. * Return true if the sequence was handled; false if * we should try a previous handler (set by addDcsHandler or setDcsHandler). @@ -554,7 +554,7 @@ declare module 'xterm' { * There is currently no way to intercept smaller data chunks, those will be stored up * until the sequence is finished. Since OSC sequences are not limited by the amount * of data this might impose a problem for big payloads. Currently xterm.js limits - * OSC payload to 50 MB which should give enough room for most use cases. + * OSC payload to 10 MB which should give enough room for most use cases. * The callback is called with OSC data string. Return true if the sequence was handled; * false if we should try a previous handler (set by addOscHandler or * setOscHandler). The most recently-added handler is tried first.