Fix "RangeError: Maximum call stack size exceeded" in hasBinary
Background
serialport 9.2.0
socket.io 4.3.0
import { createServer } from 'socket.io';
import SerialPort from 'serialport';
const serialPort = new SerialPort('/dev/ttyUSB0');
const io = createServer(8080);
serialPort.on('open', () => {
io.on('connection', (socket) => {
// this works
serialPort.on('data', (data) => {
socket.emit('data', data);
});
// but this does not
serialPort.pipe(socket);
});
});
When piping a SerialPort instance to a socket.io Socket, the following occurs:
RangeError: Maximum call stack size exceeded
at hasBinary (/code/project/node_modules/socket.io-parser/dist/is-binary.js:28:19)
at hasBinary (/code/project/node_modules/socket.io-parser/dist/is-binary.js:49:63)
at hasBinary (/code/project/node_modules/socket.io-parser/dist/is-binary.js:49:63)
Investigating further, I found this issue which was resolved by patching socket.io-parser like this. I cleaned up and modernized that patch for this repo. All credit to the original authors for their fix.
Changes
- Use flatted to parse and stringify JSON, allowing circular objects to be handled
-
Prevent infinite recursion in
_deconstructPacket -
Prevent infinite recursion in
hasBinary -
Detect
Bufferinstances as binary -
Add type hints to
reconstructPacket - Update existing test case now that exceptions are not thrown for circular objects
- Add test case to ensure that circular data is flattened appropriately
I tried to fix the RangeError without adding flatted, but it seems to be necessary as part of this solution. Hopefully the small additional dependency is worth the benefits in terms of preventing infinite recursion.
Hi! Thanks for your work on this.
I'm afraid of the increase of the package size in the browser, but we could release this under another package name (and provide it as a custom parser). What do you think?
I seem to be having the issue this is fixing.
Hi! Thanks for your work on this.
I'm afraid of the increase of the package size in the browser, but we could release this under another package name (and provide it as a custom parser). What do you think?
flatted is 0.5kb gzipped - it wouldn't be a concern for me, but up to you as to how it's handled.
It could potentially be broken out as two separate changes - handling circular JSON structures (which would be a custom parser) and "fixing the RangeError itself" (which would go into socket.io-parser) but I wasn't able to successfully separate the concerns on my first try.
I'd rather try again to split them up into separate branches than make a custom parser which overrides bits and pieces of the existing one (I can see that being fairly messy) in order to both fix the RangeError and provide circular JSON support.
@darrachequesne Any thoughts?