Skip to content

Commit 521dc25

Browse files
apapirovskiMylesBorins
authored andcommitted
tls: properly track writeQueueSize during writes
Make writeQueueSize represent the actual size of the write queue within the TLS socket. Add tls test to confirm that bufferSize works as expected. Backport-PR-URL: #16420 PR-URL: #15791 Fixes: #15005 Refs: #15006 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
1 parent b6ce918 commit 521dc25

File tree

4 files changed

+68
-5
lines changed

4 files changed

+68
-5
lines changed

lib/_tls_wrap.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,8 @@ TLSSocket.prototype._init = function(socket, wrap) {
401401
var ssl = this._handle;
402402

403403
// lib/net.js expect this value to be non-zero if write hasn't been flushed
404-
// immediately
405-
// TODO(indutny): revise this solution, it might be 1 before handshake and
406-
// represent real writeQueueSize during regular writes.
404+
// immediately. After the handshake is done this will represent the actual
405+
// write queue size
407406
ssl.writeQueueSize = 1;
408407

409408
this.server = options.server;

src/tls_wrap.cc

+22-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ using v8::Exception;
2121
using v8::Function;
2222
using v8::FunctionCallbackInfo;
2323
using v8::FunctionTemplate;
24+
using v8::Integer;
2425
using v8::Local;
2526
using v8::Object;
2627
using v8::String;
@@ -276,6 +277,7 @@ void TLSWrap::EncOut() {
276277

277278
// No data to write
278279
if (BIO_pending(enc_out_) == 0) {
280+
UpdateWriteQueueSize();
279281
if (clear_in_->Length() == 0)
280282
InvokeQueued(0);
281283
return;
@@ -530,6 +532,18 @@ bool TLSWrap::IsClosing() {
530532
}
531533

532534

535+
uint32_t TLSWrap::UpdateWriteQueueSize(uint32_t write_queue_size) {
536+
HandleScope scope(env()->isolate());
537+
if (write_queue_size == 0)
538+
write_queue_size = BIO_pending(enc_out_);
539+
object()->Set(env()->context(),
540+
env()->write_queue_size_string(),
541+
Integer::NewFromUnsigned(env()->isolate(),
542+
write_queue_size)).FromJust();
543+
return write_queue_size;
544+
}
545+
546+
533547
int TLSWrap::ReadStart() {
534548
return stream_->ReadStart();
535549
}
@@ -570,8 +584,12 @@ int TLSWrap::DoWrite(WriteWrap* w,
570584
ClearOut();
571585
// However, if there is any data that should be written to the socket,
572586
// the callback should not be invoked immediately
573-
if (BIO_pending(enc_out_) == 0)
587+
if (BIO_pending(enc_out_) == 0) {
588+
// net.js expects writeQueueSize to be > 0 if the write isn't
589+
// immediately flushed
590+
UpdateWriteQueueSize(1);
574591
return stream_->DoWrite(w, bufs, count, send_handle);
592+
}
575593
}
576594

577595
// Queue callback to execute it on next tick
@@ -621,13 +639,15 @@ int TLSWrap::DoWrite(WriteWrap* w,
621639

622640
// Try writing data immediately
623641
EncOut();
642+
UpdateWriteQueueSize();
624643

625644
return 0;
626645
}
627646

628647

629648
void TLSWrap::OnAfterWriteImpl(WriteWrap* w, void* ctx) {
630-
// Intentionally empty
649+
TLSWrap* wrap = static_cast<TLSWrap*>(ctx);
650+
wrap->UpdateWriteQueueSize();
631651
}
632652

633653

src/tls_wrap.h

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class TLSWrap : public AsyncWrap,
111111

112112
AsyncWrap* GetAsyncWrap() override;
113113
bool IsIPCPipe() override;
114+
uint32_t UpdateWriteQueueSize(uint32_t write_queue_size = 0);
114115

115116
// Resource implementation
116117
static void OnAfterWriteImpl(WriteWrap* w, void* ctx);

test/parallel/test-tls-buffersize.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
const assert = require('assert');
6+
const fixtures = require('../common/fixtures');
7+
const tls = require('tls');
8+
9+
const iter = 10;
10+
const overhead = 30;
11+
12+
const server = tls.createServer({
13+
key: fixtures.readKey('agent2-key.pem'),
14+
cert: fixtures.readKey('agent2-cert.pem')
15+
}, common.mustCall((socket) => {
16+
socket.on('readable', common.mustCallAtLeast(() => {
17+
socket.read();
18+
}, 1));
19+
20+
socket.on('end', common.mustCall(() => {
21+
server.close();
22+
}));
23+
}));
24+
25+
server.listen(0, common.mustCall(() => {
26+
const client = tls.connect({
27+
port: server.address().port,
28+
rejectUnauthorized: false
29+
}, common.mustCall(() => {
30+
assert.strictEqual(client.bufferSize, 0);
31+
32+
for (let i = 1; i < iter; i++) {
33+
client.write('a');
34+
assert.strictEqual(client.bufferSize, i + overhead);
35+
}
36+
37+
client.on('finish', common.mustCall(() => {
38+
assert.strictEqual(client.bufferSize, 0);
39+
}));
40+
41+
client.end();
42+
}));
43+
}));

0 commit comments

Comments
 (0)