Skip to content

Commit 318f1cd

Browse files
oyydjasnell
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 51fd867 commit 318f1cd

File tree

2 files changed

+53
-4
lines changed

2 files changed

+53
-4
lines changed

Diff for: lib/internal/wrap_js_stream.js

+3-4
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

Diff for: test/parallel/test-stream-wrap-drain.js

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

0 commit comments

Comments
 (0)