Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,14 +315,20 @@ OutgoingMessage.prototype.uncork = function uncork() {
callbacks.push(buf[n + 2]);
}
}
this._send(crlf_buf, null, callbacks.length ? (err) => {
const ret = this._send(crlf_buf, null, callbacks.length ? (err) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const ret = this._send(crlf_buf, null, callbacks.length ? (err) => {
this._send(crlf_buf, null, callbacks.length ? (err) => {

for (const callback of callbacks) {
callback(err);
}
} : null);

this[kChunkedBuffer].length = 0;
this[kChunkedLength] = 0;

// If we successfully flushed and had pending drain, emit it
if (ret && this[kNeedDrain]) {
this[kNeedDrain] = false;
this.emit('drain');
}
Comment on lines +328 to +331

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (ret && this[kNeedDrain]) {
this[kNeedDrain] = false;
this.emit('drain');
}
if (this[kNeedDrain] && this.writableLength === 0) {
this[kNeedDrain] = false;
this.emit('drain');
}

};

OutgoingMessage.prototype.setTimeout = function setTimeout(msecs, callback) {
Expand Down
67 changes: 67 additions & 0 deletions test/parallel/test-http-response-drain-cork.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use strict';
const common = require('../common');
const http = require('http');
const assert = require('assert');

// Test that drain event is emitted correctly when using cork/uncork
// with ServerResponse and the write buffer is full

const server = http.createServer(common.mustCall(async (req, res) => {
res.cork();

// Write small amount - won't need drain
assert.strictEqual(res.write('1'.repeat(100)), true);

// Write large amount that should require drain
assert.strictEqual(res.write('2'.repeat(1000000)), false);

// Verify writableNeedDrain is set
assert.strictEqual(res.writableNeedDrain, true);

// Wait for drain event after uncorking
const drainPromise = new Promise((resolve) => {
res.once('drain', common.mustCall(() => {
// After drain, writableNeedDrain should be false
assert.strictEqual(res.writableNeedDrain, false);
resolve();
}));
});

// Uncork should trigger drain if needed
res.uncork();

await drainPromise;

// Cork again for next write
res.cork();

// Write more data
res.write('3'.repeat(100));

// Final uncork and end
res.uncork();
res.end();
}));

server.listen(0, common.mustCall(() => {
http.get({
port: server.address().port,
}, common.mustCall((res) => {
Comment on lines +46 to +49
let data = '';
res.setEncoding('utf8');

res.on('data', (chunk) => {
data += chunk;
});

res.on('end', common.mustCall(() => {
// Verify we got all the data
assert.strictEqual(data.length, 100 + 1000000 + 100);
assert.strictEqual(data.substring(0, 100), '1'.repeat(100));
assert.strictEqual(data.substring(100, 1000100), '2'.repeat(1000000));
assert.strictEqual(data.substring(1000100), '3'.repeat(100));

server.close();
}));
}));
}));
Loading