diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 178a3418dace0a..1215624930d9ee 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -82,6 +82,8 @@ let debug = require('internal/util/debuglog').debuglog('http', (fn) => { }); const kCorked = Symbol('corked'); +const kChunkedBuffer = Symbol('kChunkedBuffer'); +const kChunkedLength = Symbol('kChunkedLength'); const kUniqueHeaders = Symbol('kUniqueHeaders'); const kBytesWritten = Symbol('kBytesWritten'); const kErrored = Symbol('errored'); @@ -140,6 +142,8 @@ function OutgoingMessage(options) { this.finished = false; this._headerSent = false; this[kCorked] = 0; + this[kChunkedBuffer] = []; + this[kChunkedLength] = 0; this._closed = false; this.socket = null; @@ -206,8 +210,7 @@ ObjectDefineProperty(OutgoingMessage.prototype, 'writableHighWaterMark', { ObjectDefineProperty(OutgoingMessage.prototype, 'writableCorked', { __proto__: null, get() { - const corked = this.socket ? this.socket.writableCorked : 0; - return corked + this[kCorked]; + return this[kCorked]; }, }); @@ -299,19 +302,49 @@ OutgoingMessage.prototype._renderHeaders = function _renderHeaders() { }; OutgoingMessage.prototype.cork = function() { + this[kCorked]++; if (this.socket) { this.socket.cork(); - } else { - this[kCorked]++; } }; OutgoingMessage.prototype.uncork = function() { + this[kCorked]--; if (this.socket) { this.socket.uncork(); - } else if (this[kCorked]) { - this[kCorked]--; } + + if (this[kCorked] || this[kChunkedBuffer].length === 0) { + return; + } + + const len = this[kChunkedLength]; + const buf = this[kChunkedBuffer]; + + if (this.chunkedEncoding) { + let callbacks; + this._send(NumberPrototypeToString(len, 16), 'latin1', null); + this._send(crlf_buf, null, null); + for (let n = 0; n < buf.length; n += 3) { + this._send(buf[n + 0], buf[n + 1], null); + if (buf[n + 2]) { + callbacks ??= []; + callbacks.push(buf[n + 2]); + } + } + this._send(crlf_buf, null, callbacks.length ? (err) => { + for (const callback of callbacks) { + callback(err); + } + } : null); + } else { + for (let n = 0; n < buf.length; n += 3) { + this._send(buf[n + 0], buf[n + 1], buf[n + 2]); + } + } + + this[kChunkedBuffer].length = 0; + this[kChunkedLength] = 0; }; OutgoingMessage.prototype.setTimeout = function setTimeout(msecs, callback) { @@ -936,7 +969,12 @@ function write_(msg, chunk, encoding, callback, fromEnd) { } let ret; - if (msg.chunkedEncoding && chunk.length !== 0) { + if (msg[kCorked] && msg._headerSent) { + len ??= typeof chunk === 'string' ? Buffer.byteLength(chunk, encoding) : chunk.byteLength; + msg[kChunkedBuffer].push(chunk, encoding, callback); + msg[kChunkedLength] += len; + ret = msg[kChunkedLength] < msg[kHighWaterMark]; + } else if (msg.chunkedEncoding && chunk.length !== 0) { len ??= typeof chunk === 'string' ? Buffer.byteLength(chunk, encoding) : chunk.byteLength; msg._send(NumberPrototypeToString(len, 16), 'latin1', null); msg._send(crlf_buf, null, null); @@ -1068,7 +1106,8 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) { this.socket._writableState.corked = 1; this.socket.uncork(); } - this[kCorked] = 0; + this[kCorked] = 1; + this.uncork(); this.finished = true;