diff --git a/lib/net.js b/lib/net.js index c5a5b357acd7b8..bb0072fa736cf7 100644 --- a/lib/net.js +++ b/lib/net.js @@ -154,8 +154,9 @@ function Socket(options) { initSocketHandle(this); - this._pendingData = null; - this._pendingEncoding = ''; + // Buffer for the single possible write while connecting: when uncorked + // or ended, then written to. Writable will buffer any other writes. + this._pendingWrite = null; // handle strings directly this._writableState.decodeStrings = false; @@ -628,19 +629,18 @@ Socket.prototype.write = function(chunk, encoding, cb) { Socket.prototype._writeGeneric = function(writev, data, encoding, cb) { - // If we are still connecting, then buffer this for later. - // The Writable logic will buffer up any more writes while - // waiting for this one to be done. + // If the socket is ended / uncorked while connecting, a single write can + // fire through the cork. Buffer it and send it once connected. Writable + // will buffer any sequential writes if uncorked. if (this._connecting) { - this._pendingData = data; - this._pendingEncoding = encoding; + this._pendingWrite = { data: data, encoding: encoding }; + this.once('connect', function() { + this._pendingWrite = null; this._writeGeneric(writev, data, encoding, cb); }); return; } - this._pendingData = null; - this._pendingEncoding = ''; this._unrefTimer(); @@ -730,8 +730,6 @@ function createWriteReq(req, handle, data, encoding) { Socket.prototype.__defineGetter__('bytesWritten', function() { var bytes = this._bytesDispatched; const state = this._writableState; - const data = this._pendingData; - const encoding = this._pendingEncoding; if (!state) return undefined; @@ -743,11 +741,24 @@ Socket.prototype.__defineGetter__('bytesWritten', function() { bytes += Buffer.byteLength(el.chunk, el.encoding); }); - if (data) { - if (data instanceof Buffer) - bytes += data.length; - else - bytes += Buffer.byteLength(data, encoding); + var buffer = this._pendingWrite; + if (Array.isArray(buffer)) { + // was a writev, iterate over chunks to get total length + for (let i = 0; i < buffer.length; i++) { + let chunk = buffer[i]; + + if (chunk instanceof Buffer) { + bytes += chunk.length; + } else { + bytes += Buffer.byteLength(chunk.data, chunk.encoding); + } + } + } else if (buffer) { + if (buffer.data instanceof Buffer) { + bytes += buffer.data.length; + } else { + bytes += Buffer.byteLength(buffer.data, buffer.encoding); + } } return bytes; @@ -869,6 +880,7 @@ Socket.prototype.connect = function(options, cb) { } if (this.destroyed) { + // Recycle the socket. this._readableState.reading = false; this._readableState.ended = false; this._readableState.endEmitted = false; @@ -899,6 +911,12 @@ Socket.prototype.connect = function(options, cb) { this._connecting = true; this.writable = true; + // Cork then uncork upon connecting. + this.cork(); + this.once('connect', function() { + this.uncork(); + }); + if (pipe) { connect(this, options.path); } else { diff --git a/test/parallel/test-net-socket-byteswritten.js b/test/parallel/test-net-socket-byteswritten.js new file mode 100644 index 00000000000000..3a8b40f6f605bd --- /dev/null +++ b/test/parallel/test-net-socket-byteswritten.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const Buffer = require('buffer').Buffer; + +var server = net.createServer(function(socket) { + socket.end(); +}); + +server.listen(common.PORT, common.mustCall(function() { + var socket = net.connect(common.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.equal(socket.bytesWritten, 3 + 4); + + socket.on('connect', common.mustCall(function() { + assert.equal(socket.bytesWritten, 3 + 4); + })); + + socket.on('end', common.mustCall(function() { + assert.equal(socket.bytesWritten, 3 + 4); + + server.close(); + })); +}));