Skip to content

Commit 1afd207

Browse files
oyydMylesBorins
authored andcommitted
tls: make StreamWrap work correctly in "drain" callback
When an instance of StreamWrap is shutting down and a "drain" event is emitted, the instance will abort as its `this[kCurrentShutdownRequest]` is already set. The following test will fail before this commit. PR-URL: #23294 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]>
1 parent 2564abf commit 1afd207

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

lib/internal/wrap_js_stream.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,6 @@ class JSStreamWrap extends Socket {
100100
}
101101

102102
doShutdown(req) {
103-
assert.strictEqual(this[kCurrentShutdownRequest], null);
104-
this[kCurrentShutdownRequest] = req;
105-
106103
// TODO(addaleax): It might be nice if we could get into a state where
107104
// DoShutdown() is not called on streams while a write is still pending.
108105
//
@@ -113,8 +110,10 @@ class JSStreamWrap extends Socket {
113110
// so for now that is supported here.
114111

115112
if (this[kCurrentWriteRequest] !== null)
116-
return this.on('drain', () => this.doShutdown(req));
113+
return this.once('drain', () => this.doShutdown(req));
117114
assert.strictEqual(this[kCurrentWriteRequest], null);
115+
assert.strictEqual(this[kCurrentShutdownRequest], null);
116+
this[kCurrentShutdownRequest] = req;
118117

119118
const handle = this._handle;
120119

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const { StreamWrap } = require('_stream_wrap');
5+
const { Duplex } = require('stream');
6+
const { ShutdownWrap } = process.binding('stream_wrap');
7+
8+
// This test makes sure that when an instance of JSStreamWrap is waiting for
9+
// a "drain" event to `doShutdown`, the instance will work correctly when a
10+
// "drain" event emitted.
11+
{
12+
let resolve = null;
13+
14+
class TestDuplex extends Duplex {
15+
_write(chunk, encoding, callback) {
16+
// We will resolve the write later.
17+
resolve = () => {
18+
callback();
19+
};
20+
}
21+
22+
_read() {}
23+
}
24+
25+
const testDuplex = new TestDuplex();
26+
const socket = new StreamWrap(testDuplex);
27+
28+
socket.write(
29+
// Make the buffer long enough so that the `Writable` will emit "drain".
30+
Buffer.allocUnsafe(socket.writableHighWaterMark * 2),
31+
common.mustCall()
32+
);
33+
34+
// Make sure that the 'drain' events will be emitted.
35+
testDuplex.on('drain', common.mustCall(() => {
36+
console.log('testDuplex drain');
37+
}));
38+
39+
assert.strictEqual(typeof resolve, 'function');
40+
41+
const req = new ShutdownWrap();
42+
req.oncomplete = common.mustCall();
43+
req.handle = socket._handle;
44+
// Should not throw.
45+
socket._handle.shutdown(req);
46+
47+
resolve();
48+
}

0 commit comments

Comments
 (0)