From 85a5e5a1f29246f7386f43ea280f2fe0e6ad053a Mon Sep 17 00:00:00 2001 From: Brendan Ashworth Date: Thu, 13 Jul 2017 10:36:44 -0800 Subject: [PATCH] net: fix bytesWritten during writev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a writev is caused on a socket (sometimes through corking and uncorking), previously net would call Buffer.byteLength on the array of buffers and chunks. This throws a TypeError, because Buffer.byteLength throws when passed a non-string. In dbfe8c4e, behavior changed to throw when passed a non-string. This is correct behavior. Previously, it would cast the argument to a string, so before this commit, bytesWritten would give an erroneous value. This commit corrects the behavior equally both before and after dbfe8c4e. This commit fixes this bug by iterating over each chunk in the pending stack and calculating the length individually. Also adds a regression test. This additionally changes an `instanceof Buffer` check to `typeof !== 'string'`, which should be equivalent. PR-URL: https://github.com/nodejs/node/pull/14236 Reviewed-By: Brian White Reviewed-By: Luigi Pinca Reviewed-By: Tobias Nießen Refs: https://github.com/nodejs/node/pull/2960 --- lib/net.js | 15 ++++++-- test/parallel/test-net-socket-byteswritten.js | 35 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-net-socket-byteswritten.js diff --git a/lib/net.js b/lib/net.js index 638035cb2fa..36efd27fa02 100644 --- a/lib/net.js +++ b/lib/net.js @@ -827,8 +827,19 @@ protoGetter('bytesWritten', function bytesWritten() { bytes += Buffer.byteLength(el.chunk, el.encoding); }); - if (data) { - if (data instanceof Buffer) + if (Array.isArray(data)) { + // was a writev, iterate over chunks to get total length + for (var i = 0; i < data.length; i++) { + const chunk = data[i]; + + if (data.allBuffers || chunk instanceof Buffer) + bytes += chunk.length; + else + bytes += Buffer.byteLength(chunk.chunk, chunk.encoding); + } + } else if (data) { + // Writes are either a string or a Buffer. + if (typeof data !== 'string') bytes += data.length; else bytes += Buffer.byteLength(data, encoding); diff --git a/test/parallel/test-net-socket-byteswritten.js b/test/parallel/test-net-socket-byteswritten.js new file mode 100644 index 00000000000..6f3ce8a3c6c --- /dev/null +++ b/test/parallel/test-net-socket-byteswritten.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(function(socket) { + socket.end(); +}); + +server.listen(0, common.mustCall(function() { + const socket = net.connect(server.address().port); + + // Cork the socket, then write twice; this should cause a writev, which + // previously caused an err in the bytesWritten count. + socket.cork(); + + socket.write('one'); + socket.write(new Buffer('twø', 'utf8')); + + socket.uncork(); + + // one = 3 bytes, twø = 4 bytes + assert.strictEqual(socket.bytesWritten, 3 + 4); + + socket.on('connect', common.mustCall(function() { + assert.strictEqual(socket.bytesWritten, 3 + 4); + })); + + socket.on('end', common.mustCall(function() { + assert.strictEqual(socket.bytesWritten, 3 + 4); + + server.close(); + })); +}));