diff --git a/doc/api.md b/doc/api.md index 465ed66..ddee454 100644 --- a/doc/api.md +++ b/doc/api.md @@ -66,6 +66,20 @@ Create a parser of `mainType` defined in `proto`. This is a Transform stream. Returns a parsed packet of `buffer`. +## FullPacketParser(proto,mainType,noErrorLogging) + +Create a parser of `mainType` defined in `proto` that reads one whole packet per chunk. This is a Transform stream. + +A chunk the definitions cannot read is dropped and the stream continues. Its stack is logged unless `noErrorLogging` is set. + +### FullPacketParser.parsePacketBuffer(buffer) + +Returns a parsed packet of `buffer`. + +### Event: 'partialReadError' (error) + +Emitted for each dropped chunk, before it is logged. `error.buffer` is the chunk. + ## types An object mapping the default type names to the corresponding `[read,write,sizeOf]` functions. diff --git a/index.d.ts b/index.d.ts index 70180af..19019e0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -109,6 +109,8 @@ declare class CompiledProtoDef extends AbstractProtoDefInterface { declare class ProtodefPartialError extends Error { partialReadError: true + // The chunk a FullPacketParser could not read; set when it emits 'partialReadError'. + buffer?: Buffer constructor(message?: string) } @@ -134,6 +136,8 @@ declare module 'protodef' { noErrorLogging: boolean constructor(proto: ProtoDef, mainType: string, noErrorLogging = false) parsePacketBuffer(packet: any): Buffer + on(event: 'partialReadError', listener: (error: ProtodefPartialError) => void): this + on(event: string | symbol, listener: (...args: any[]) => void): this } export const Compiler: { ReadCompiler: typeof ProtodefReadCompiler diff --git a/src/serializer.js b/src/serializer.js index 8b52e95..d744141 100644 --- a/src/serializer.js +++ b/src/serializer.js @@ -78,6 +78,8 @@ class FullPacketParser extends Transform { } } catch (e) { if (e.partialReadError) { + e.buffer = chunk + this.emit('partialReadError', e) if (!this.noErrorLogging) { console.log(e.stack) } diff --git a/test/misc.js b/test/misc.js index dab6359..fc726e9 100644 --- a/test/misc.js +++ b/test/misc.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ const assert = require('assert') -const { ProtoDef } = require('../') +const { ProtoDef, FullPacketParser } = require('../') const { ProtoDefCompiler } = require('../').Compiler it('example works', () => { @@ -25,3 +25,29 @@ describe('mapper', () => { }) } }) + +describe('FullPacketParser', () => { + const packet = ['container', [{ name: 'a', type: 'i32' }]] + const proto = new ProtoDef() + proto.addType('packet', packet) + const compiler = new ProtoDefCompiler() + compiler.addTypesToCompile({ packet }) + const compiled = compiler.compileProtoDefSync() + + for (const [label, p] of [['interpreted', proto], ['compiled', compiled]]) { + it(`emits partialReadError with the chunk it could not read, and keeps parsing (${label})`, async () => { + const parser = new FullPacketParser(p, 'packet', true) + const errors = [] + const packets = [] + parser.on('partialReadError', e => errors.push(e)) + parser.on('data', d => packets.push(d.data)) + parser.write(Buffer.from([0, 0])) + parser.write(Buffer.from([0, 0, 0, 7])) + await new Promise(resolve => parser.end(resolve)) + assert.strictEqual(errors.length, 1) + assert.strictEqual(errors[0].partialReadError, true) + assert.deepStrictEqual(errors[0].buffer, Buffer.from([0, 0])) + assert.deepStrictEqual(packets, [{ a: 7 }]) + }) + } +})