Skip to content

Commit 923fb5c

Browse files
committed
net: track bytesWritten in C++ land
Move tracking of `socket.bytesWritten` to C++ land. This makes it easier to provide this functionality for all `StreamBase` instances, and in particular should keep working when they have been 'consumed' in C++ in some way (e.g. for the network sockets that are underlying to TLS or HTTP2 streams). Also, this parallels `socket.bytesRead` a lot more now. PR-URL: #19551 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent abc8786 commit 923fb5c

File tree

6 files changed

+54
-10
lines changed

6 files changed

+54
-10
lines changed

lib/internal/net.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function makeSyncWrite(fd) {
3232
if (enc !== 'buffer')
3333
chunk = Buffer.from(chunk, enc);
3434

35-
this._bytesDispatched += chunk.length;
35+
this._handle.bytesWritten += chunk.length;
3636

3737
const ctx = {};
3838
writeBuffer(fd, chunk, 0, chunk.length, null, undefined, ctx);

lib/net.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ function normalizeArgs(args) {
206206
// called when creating new Socket, or when re-using a closed Socket
207207
function initSocketHandle(self) {
208208
self._undestroy();
209-
self._bytesDispatched = 0;
210209
self._sockname = null;
211210

212211
// Handle creation may be deferred to bind() or connect() time.
@@ -222,7 +221,8 @@ function initSocketHandle(self) {
222221
}
223222

224223

225-
const BYTES_READ = Symbol('bytesRead');
224+
const kBytesRead = Symbol('kBytesRead');
225+
const kBytesWritten = Symbol('kBytesWritten');
226226

227227

228228
function Socket(options) {
@@ -278,6 +278,11 @@ function Socket(options) {
278278

279279
this._writev = null;
280280
this._write = makeSyncWrite(fd);
281+
// makeSyncWrite adjusts this value like the original handle would, so
282+
// we need to let it do that by turning it into a writable, own property.
283+
Object.defineProperty(this._handle, 'bytesWritten', {
284+
value: 0, writable: true
285+
});
281286
}
282287
} else {
283288
// these will be set once there is a connection
@@ -316,7 +321,8 @@ function Socket(options) {
316321
this._server = null;
317322

318323
// Used after `.destroy()`
319-
this[BYTES_READ] = 0;
324+
this[kBytesRead] = 0;
325+
this[kBytesWritten] = 0;
320326
}
321327
util.inherits(Socket, stream.Duplex);
322328

@@ -588,8 +594,9 @@ Socket.prototype._destroy = function(exception, cb) {
588594
if (this !== process.stderr)
589595
debug('close handle');
590596
var isException = exception ? true : false;
591-
// `bytesRead` should be accessible after `.destroy()`
592-
this[BYTES_READ] = this._handle.bytesRead;
597+
// `bytesRead` and `kBytesWritten` should be accessible after `.destroy()`
598+
this[kBytesRead] = this._handle.bytesRead;
599+
this[kBytesWritten] = this._handle.bytesWritten;
593600

594601
this._handle.close(() => {
595602
debug('emit close');
@@ -689,7 +696,7 @@ function protoGetter(name, callback) {
689696
}
690697

691698
protoGetter('bytesRead', function bytesRead() {
692-
return this._handle ? this._handle.bytesRead : this[BYTES_READ];
699+
return this._handle ? this._handle.bytesRead : this[kBytesRead];
693700
});
694701

695702
protoGetter('remoteAddress', function remoteAddress() {
@@ -761,8 +768,6 @@ Socket.prototype._writeGeneric = function(writev, data, encoding, cb) {
761768
// Bail out if handle.write* returned an error
762769
if (ret) return ret;
763770

764-
this._bytesDispatched += req.bytes;
765-
766771
if (!req.async) {
767772
cb();
768773
return;
@@ -782,6 +787,13 @@ Socket.prototype._write = function(data, encoding, cb) {
782787
this._writeGeneric(false, data, encoding, cb);
783788
};
784789

790+
791+
// Legacy alias. Having this is probably being overly cautious, but it doesn't
792+
// really hurt anyone either. This can probably be removed safely if desired.
793+
protoGetter('_bytesDispatched', function _bytesDispatched() {
794+
return this._handle ? this._handle.bytesWritten : this[kBytesWritten];
795+
});
796+
785797
protoGetter('bytesWritten', function bytesWritten() {
786798
var bytes = this._bytesDispatched;
787799
const state = this._writableState;

src/env.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ struct PackageConfig {
117117
V(bytes_string, "bytes") \
118118
V(bytes_parsed_string, "bytesParsed") \
119119
V(bytes_read_string, "bytesRead") \
120+
V(bytes_written_string, "bytesWritten") \
120121
V(cached_data_string, "cachedData") \
121122
V(cached_data_produced_string, "cachedDataProduced") \
122123
V(cached_data_rejected_string, "cachedDataRejected") \

src/stream_base-inl.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ inline StreamWriteResult StreamBase::Write(
193193
v8::Local<v8::Object> req_wrap_obj) {
194194
Environment* env = stream_env();
195195
int err;
196+
197+
for (size_t i = 0; i < count; ++i)
198+
bytes_written_ += bufs[i].len;
199+
196200
if (send_handle == nullptr) {
197201
err = DoTryWrite(&bufs, &count);
198202
if (err != 0 || count == 0) {
@@ -301,6 +305,12 @@ void StreamBase::AddMethods(Environment* env,
301305
env->as_external(),
302306
signature);
303307

308+
Local<FunctionTemplate> get_bytes_written_templ =
309+
FunctionTemplate::New(env->isolate(),
310+
GetBytesWritten<Base>,
311+
env->as_external(),
312+
signature);
313+
304314
t->PrototypeTemplate()->SetAccessorProperty(env->fd_string(),
305315
get_fd_templ,
306316
Local<FunctionTemplate>(),
@@ -316,6 +326,11 @@ void StreamBase::AddMethods(Environment* env,
316326
Local<FunctionTemplate>(),
317327
attributes);
318328

329+
t->PrototypeTemplate()->SetAccessorProperty(env->bytes_written_string(),
330+
get_bytes_written_templ,
331+
Local<FunctionTemplate>(),
332+
attributes);
333+
319334
env->SetProtoMethod(t, "readStart", JSMethod<Base, &StreamBase::ReadStartJS>);
320335
env->SetProtoMethod(t, "readStop", JSMethod<Base, &StreamBase::ReadStopJS>);
321336
if ((flags & kFlagNoShutdown) == 0)
@@ -357,7 +372,6 @@ void StreamBase::GetFD(const FunctionCallbackInfo<Value>& args) {
357372

358373
template <class Base>
359374
void StreamBase::GetBytesRead(const FunctionCallbackInfo<Value>& args) {
360-
// The handle instance hasn't been set. So no bytes could have been read.
361375
Base* handle;
362376
ASSIGN_OR_RETURN_UNWRAP(&handle,
363377
args.This(),
@@ -368,6 +382,18 @@ void StreamBase::GetBytesRead(const FunctionCallbackInfo<Value>& args) {
368382
args.GetReturnValue().Set(static_cast<double>(wrap->bytes_read_));
369383
}
370384

385+
template <class Base>
386+
void StreamBase::GetBytesWritten(const FunctionCallbackInfo<Value>& args) {
387+
Base* handle;
388+
ASSIGN_OR_RETURN_UNWRAP(&handle,
389+
args.This(),
390+
args.GetReturnValue().Set(0));
391+
392+
StreamBase* wrap = static_cast<StreamBase*>(handle);
393+
// uint64_t -> double. 53bits is enough for all real cases.
394+
args.GetReturnValue().Set(static_cast<double>(wrap->bytes_written_));
395+
}
396+
371397
template <class Base>
372398
void StreamBase::GetExternal(const FunctionCallbackInfo<Value>& args) {
373399
Base* handle;

src/stream_base.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ int StreamBase::WriteString(const FunctionCallbackInfo<Value>& args) {
243243
uv_buf_t* bufs = &buf;
244244
size_t count = 1;
245245
err = DoTryWrite(&bufs, &count);
246+
bytes_written_ += data_size;
246247

247248
// Immediate failure or success
248249
if (err != 0 || count == 0) {

src/stream_base.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ class StreamResource {
247247

248248
StreamListener* listener_ = nullptr;
249249
uint64_t bytes_read_ = 0;
250+
uint64_t bytes_written_ = 0;
250251

251252
friend class StreamListener;
252253
};
@@ -324,6 +325,9 @@ class StreamBase : public StreamResource {
324325
template <class Base>
325326
static void GetBytesRead(const v8::FunctionCallbackInfo<v8::Value>& args);
326327

328+
template <class Base>
329+
static void GetBytesWritten(const v8::FunctionCallbackInfo<v8::Value>& args);
330+
327331
template <class Base,
328332
int (StreamBase::*Method)(
329333
const v8::FunctionCallbackInfo<v8::Value>& args)>

0 commit comments

Comments
 (0)