stream: writableNeedDrain

Don't write to a stream which already has a full buffer.

Fixes: https://github.com/nodejs/node/issues/35341

PR-URL: https://github.com/nodejs/node/pull/35348
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
Robert Nagy 2020-09-25 18:40:01 +02:00 committed by Antoine du Hamel
parent da53a3caa3
commit dd0f8f18c2
7 changed files with 63 additions and 1 deletions

View File

@ -580,6 +580,15 @@ This property contains the number of bytes (or objects) in the queue
ready to be written. The value provides introspection data regarding ready to be written. The value provides introspection data regarding
the status of the `highWaterMark`. the status of the `highWaterMark`.
##### `writable.writableNeedDrain`
<!-- YAML
added: REPLACEME
-->
* {boolean}
Is `true` if the stream's buffer has been full and stream will emit `'drain'`.
##### `writable.writableObjectMode` ##### `writable.writableObjectMode`
<!-- YAML <!-- YAML
added: v12.3.0 added: v12.3.0

View File

@ -660,6 +660,11 @@ ObjectDefineProperty(OutgoingMessage.prototype, 'writableEnded', {
get: function() { return this.finished; } get: function() { return this.finished; }
}); });
ObjectDefineProperty(OutgoingMessage.prototype, 'writableNeedDrain', {
get: function() {
return !this.destroyed && !this.finished && this[kNeedDrain];
}
});
const crlf_buf = Buffer.from('\r\n'); const crlf_buf = Buffer.from('\r\n');
OutgoingMessage.prototype.write = function write(chunk, encoding, callback) { OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {

View File

@ -87,6 +87,8 @@ ObjectDefineProperties(Duplex.prototype, {
ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableCorked'), ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableCorked'),
writableEnded: writableEnded:
ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableEnded'), ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableEnded'),
writableNeedDrain:
ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableNeedDrain'),
destroyed: { destroyed: {
get() { get() {

View File

@ -123,6 +123,10 @@ async function pump(iterable, writable, finish) {
} }
let error; let error;
try { try {
if (writable.writableNeedDrain === true) {
await EE.once(writable, 'drain');
}
for await (const chunk of iterable) { for await (const chunk of iterable) {
if (!writable.write(chunk)) { if (!writable.write(chunk)) {
if (writable.destroyed) return; if (writable.destroyed) return;

View File

@ -783,7 +783,12 @@ Readable.prototype.pipe = function(dest, pipeOpts) {
dest.emit('pipe', src); dest.emit('pipe', src);
// Start the flow if it hasn't been started already. // Start the flow if it hasn't been started already.
if (!state.flowing) {
if (dest.writableNeedDrain === true) {
if (state.flowing) {
src.pause();
}
} else if (!state.flowing) {
debug('pipe resume'); debug('pipe resume');
src.resume(); src.resume();
} }

View File

@ -805,6 +805,14 @@ ObjectDefineProperties(Writable.prototype, {
} }
}, },
writableNeedDrain: {
get() {
const wState = this._writableState;
if (!wState) return false;
return !wState.destroyed && !wState.ending && wState.needDrain;
}
},
writableHighWaterMark: { writableHighWaterMark: {
get() { get() {
return this._writableState && this._writableState.highWaterMark; return this._writableState && this._writableState.highWaterMark;

View File

@ -0,0 +1,29 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const Readable = require('_stream_readable');
const Writable = require('_stream_writable');
// Pipe should not continue writing if writable needs drain.
{
const w = new Writable({
write(buf, encoding, callback) {
}
});
while (w.write('asd'));
assert.strictEqual(w.writableNeedDrain, true);
const r = new Readable({
read() {
this.push('asd');
}
});
w.write = common.mustNotCall();
r.pipe(w);
}