Description
- Version: v14.4.0
- Platform: Linux 97fdeff5e333 4.4.0-184-generic Ubuntu SMP x86_64 GNU/Linux
- Subsystem: net
What steps will reproduce the bug?
I'm using the WebSocket library ws
to handle connections from a browser over a VPN. The bufferedAmount
property of ws
uses the bufferSize
property of the underlying net.Socket object (from the http.createServer function). I've narrowed the bug down to know the incorrect value is only coming from the bufferSize
property in Node.js itself.
How often does it reproduce? Is there a required condition?
I'm able to reproduce it reliably in my Linux/Docker environment under certain (slow) network conditions.
This issue seems to happen when the socket is being written to quickly, to the point where write callbacks are getting delayed and batched up (several are called together in quick succession).
What is the expected behavior?
The bufferSize
should never be greater than the actual number of bytes that were sent to the socket.
What do you see instead?
Each time I send a WebSocket packet, I add the length of the packet onto my own 'pending' queue and I remove it from the queue when I receive the send/write callback. After I send the data to the WebSocket, I query the bufferSize
property and the writableLength
property of the underlying TCP socket.
Using this TS code:
console.log("Old pending size: " + this.pending.reduce((prev, curr) => prev + curr.byteLength, 0));
console.log("Sending data of length: " + dataByteLength);
this.socket.send(data, cb);
this.pending.unshift(newPendingRecord);
console.log("New pending size: " + this.pending.reduce((prev, curr) => prev + curr.byteLength, 0));
...
const pending: DataRecord[] = this.pending;
const pendingBytes: number = pending.reduce((prev, curr) => prev + curr.byteLength, 0);
const sock: Socket = (this.socket as any)._socket; // get the underlying TCP socket from the WebSocket
const bufferSize: number = sock.bufferSize;
const bytesSent: number = pendingBytes - bufferSize;
console.log("Pending bytes (" + pendingBytes + ") - bufferSize (" + bufferSize + ") = " + bytesSent);
console.log("writableLength: " + sock.writableLength);
I get this output:
Old pending size: 0
Sending data of length: 56205
New pending size: 56205
Pending bytes (56205) - bufferSize (112418) = -56213
writableLength: 56209
Additional information
I tracked down the code in Node.js that implements the bufferSize
property: https://github.com/nodejs/node/blob/master/lib/net.js#L544
Using the printed value of writableLength
(56209), that means the value of this[kLastWriteQueueSize]
is also 56209. My guess is that one of the values is not getting cleared at the right time. Maybe it's because the Node.js event loop is getting held up by all of the other write requests? I don't know enough about Node.js internals to keep debugging this issue further.