http: improve outgoing string write performance
PR-URL: https://github.com/nodejs/node/pull/13013 Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Родитель
329b6e908c
Коммит
a10bdb51b1
|
@ -78,6 +78,9 @@ utcDate._onTimeout = function _onTimeout() {
|
|||
};
|
||||
|
||||
|
||||
function noopPendingOutput(amount) {}
|
||||
|
||||
|
||||
function OutgoingMessage() {
|
||||
Stream.call(this);
|
||||
|
||||
|
@ -117,7 +120,7 @@ function OutgoingMessage() {
|
|||
this._header = null;
|
||||
this[outHeadersKey] = null;
|
||||
|
||||
this._onPendingData = null;
|
||||
this._onPendingData = noopPendingOutput;
|
||||
}
|
||||
util.inherits(OutgoingMessage, Stream);
|
||||
|
||||
|
@ -234,12 +237,18 @@ OutgoingMessage.prototype._send = function _send(data, encoding, callback) {
|
|||
(encoding === 'utf8' || encoding === 'latin1' || !encoding)) {
|
||||
data = this._header + data;
|
||||
} else {
|
||||
this.output.unshift(this._header);
|
||||
var header = this._header;
|
||||
if (this.output.length === 0) {
|
||||
this.output = [header];
|
||||
this.outputEncodings = ['latin1'];
|
||||
this.outputCallbacks = [null];
|
||||
} else {
|
||||
this.output.unshift(header);
|
||||
this.outputEncodings.unshift('latin1');
|
||||
this.outputCallbacks.unshift(null);
|
||||
this.outputSize += this._header.length;
|
||||
if (typeof this._onPendingData === 'function')
|
||||
this._onPendingData(this._header.length);
|
||||
}
|
||||
this.outputSize += header.length;
|
||||
this._onPendingData(header.length);
|
||||
}
|
||||
this._headerSent = true;
|
||||
}
|
||||
|
@ -275,19 +284,13 @@ function _writeRaw(data, encoding, callback) {
|
|||
return conn.write(data, encoding, callback);
|
||||
}
|
||||
// Buffer, as long as we're not destroyed.
|
||||
return this._buffer(data, encoding, callback);
|
||||
}
|
||||
|
||||
|
||||
OutgoingMessage.prototype._buffer = function _buffer(data, encoding, callback) {
|
||||
this.output.push(data);
|
||||
this.outputEncodings.push(encoding);
|
||||
this.outputCallbacks.push(callback);
|
||||
this.outputSize += data.length;
|
||||
if (typeof this._onPendingData === 'function')
|
||||
this._onPendingData(data.length);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
OutgoingMessage.prototype._storeHeader = _storeHeader;
|
||||
|
@ -624,27 +627,31 @@ Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
|
|||
|
||||
const crlf_buf = Buffer.from('\r\n');
|
||||
OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {
|
||||
if (this.finished) {
|
||||
return write_(this, chunk, encoding, callback, false);
|
||||
};
|
||||
|
||||
function write_(msg, chunk, encoding, callback, fromEnd) {
|
||||
if (msg.finished) {
|
||||
var err = new Error('write after end');
|
||||
nextTick(this.socket[async_id_symbol],
|
||||
writeAfterEndNT.bind(this),
|
||||
nextTick(msg.socket[async_id_symbol],
|
||||
writeAfterEndNT.bind(msg),
|
||||
err,
|
||||
callback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this._header) {
|
||||
this._implicitHeader();
|
||||
if (!msg._header) {
|
||||
msg._implicitHeader();
|
||||
}
|
||||
|
||||
if (!this._hasBody) {
|
||||
if (!msg._hasBody) {
|
||||
debug('This type of response MUST NOT have a body. ' +
|
||||
'Ignoring write() calls.');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
|
||||
if (!fromEnd && typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
|
||||
throw new TypeError('First argument must be a string or Buffer');
|
||||
}
|
||||
|
||||
|
@ -654,38 +661,28 @@ OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {
|
|||
if (chunk.length === 0) return true;
|
||||
|
||||
var len, ret;
|
||||
if (this.chunkedEncoding) {
|
||||
if (typeof chunk === 'string' &&
|
||||
encoding !== 'hex' &&
|
||||
encoding !== 'base64' &&
|
||||
encoding !== 'latin1') {
|
||||
len = Buffer.byteLength(chunk, encoding);
|
||||
chunk = len.toString(16) + CRLF + chunk + CRLF;
|
||||
ret = this._send(chunk, encoding, callback);
|
||||
} else {
|
||||
// buffer, or a non-toString-friendly encoding
|
||||
if (msg.chunkedEncoding) {
|
||||
if (typeof chunk === 'string')
|
||||
len = Buffer.byteLength(chunk, encoding);
|
||||
else
|
||||
len = chunk.length;
|
||||
|
||||
if (this.connection && !this.connection.corked) {
|
||||
this.connection.cork();
|
||||
process.nextTick(connectionCorkNT, this.connection);
|
||||
if (msg.connection && !msg.connection.corked) {
|
||||
msg.connection.cork();
|
||||
process.nextTick(connectionCorkNT, msg.connection);
|
||||
}
|
||||
|
||||
this._send(len.toString(16), 'latin1', null);
|
||||
this._send(crlf_buf, null, null);
|
||||
this._send(chunk, encoding, null);
|
||||
ret = this._send(crlf_buf, null, callback);
|
||||
}
|
||||
msg._send(len.toString(16), 'latin1', null);
|
||||
msg._send(crlf_buf, null, null);
|
||||
msg._send(chunk, encoding, null);
|
||||
ret = msg._send(crlf_buf, null, callback);
|
||||
} else {
|
||||
ret = this._send(chunk, encoding, callback);
|
||||
ret = msg._send(chunk, encoding, callback);
|
||||
}
|
||||
|
||||
debug('write ret = ' + ret);
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function writeAfterEndNT(err, callback) {
|
||||
|
@ -736,47 +733,38 @@ function onFinish(outmsg) {
|
|||
outmsg.emit('finish');
|
||||
}
|
||||
|
||||
OutgoingMessage.prototype.end = function end(data, encoding, callback) {
|
||||
if (typeof data === 'function') {
|
||||
callback = data;
|
||||
data = null;
|
||||
OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
|
||||
if (typeof chunk === 'function') {
|
||||
callback = chunk;
|
||||
chunk = null;
|
||||
} else if (typeof encoding === 'function') {
|
||||
callback = encoding;
|
||||
encoding = null;
|
||||
}
|
||||
|
||||
if (data && typeof data !== 'string' && !(data instanceof Buffer)) {
|
||||
throw new TypeError('First argument must be a string or Buffer');
|
||||
}
|
||||
|
||||
if (this.finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var uncork;
|
||||
if (chunk) {
|
||||
if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
|
||||
throw new TypeError('First argument must be a string or Buffer');
|
||||
}
|
||||
if (!this._header) {
|
||||
if (data) {
|
||||
if (typeof data === 'string')
|
||||
this._contentLength = Buffer.byteLength(data, encoding);
|
||||
if (typeof chunk === 'string')
|
||||
this._contentLength = Buffer.byteLength(chunk, encoding);
|
||||
else
|
||||
this._contentLength = data.length;
|
||||
} else {
|
||||
this._contentLength = 0;
|
||||
this._contentLength = chunk.length;
|
||||
}
|
||||
this._implicitHeader();
|
||||
}
|
||||
|
||||
if (data && !this._hasBody) {
|
||||
debug('This type of response MUST NOT have a body. ' +
|
||||
'Ignoring data passed to end().');
|
||||
data = null;
|
||||
}
|
||||
|
||||
if (this.connection && data)
|
||||
if (this.connection) {
|
||||
this.connection.cork();
|
||||
|
||||
if (data) {
|
||||
// Normal body write.
|
||||
this.write(data, encoding);
|
||||
uncork = true;
|
||||
}
|
||||
write_(this, chunk, encoding, null, true);
|
||||
} else if (!this._header) {
|
||||
this._contentLength = 0;
|
||||
this._implicitHeader();
|
||||
}
|
||||
|
||||
if (typeof callback === 'function')
|
||||
|
@ -792,7 +780,7 @@ OutgoingMessage.prototype.end = function end(data, encoding, callback) {
|
|||
ret = this._send('', 'latin1', finish);
|
||||
}
|
||||
|
||||
if (this.connection && data)
|
||||
if (uncork)
|
||||
this.connection.uncork();
|
||||
|
||||
this.finished = true;
|
||||
|
@ -871,7 +859,6 @@ OutgoingMessage.prototype._flushOutput = function _flushOutput(socket) {
|
|||
this.output = [];
|
||||
this.outputEncodings = [];
|
||||
this.outputCallbacks = [];
|
||||
if (typeof this._onPendingData === 'function')
|
||||
this._onPendingData(-this.outputSize);
|
||||
this.outputSize = 0;
|
||||
|
||||
|
|
|
@ -23,12 +23,12 @@
|
|||
const common = require('../common');
|
||||
const http = require('http');
|
||||
|
||||
let serverRes;
|
||||
const server = http.Server(function(req, res) {
|
||||
console.log('Server accepted request.');
|
||||
serverRes = res;
|
||||
res.writeHead(200);
|
||||
res.write('Part of my res.');
|
||||
|
||||
res.destroy();
|
||||
});
|
||||
|
||||
server.listen(0, common.mustCall(function() {
|
||||
|
@ -37,6 +37,7 @@ server.listen(0, common.mustCall(function() {
|
|||
headers: { connection: 'keep-alive' }
|
||||
}, common.mustCall(function(res) {
|
||||
server.close();
|
||||
serverRes.destroy();
|
||||
|
||||
console.log(`Got res: ${res.statusCode}`);
|
||||
console.dir(res.headers);
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
const common = require('../common');
|
||||
const http = require('http');
|
||||
|
||||
let serverRes;
|
||||
const server = http.Server(function(req, res) {
|
||||
res.write('Part of my res.');
|
||||
res.destroy();
|
||||
serverRes = res;
|
||||
});
|
||||
|
||||
server.listen(0, common.mustCall(function() {
|
||||
|
@ -13,6 +14,7 @@ server.listen(0, common.mustCall(function() {
|
|||
headers: { connection: 'keep-alive' }
|
||||
}, common.mustCall(function(res) {
|
||||
server.close();
|
||||
serverRes.destroy();
|
||||
res.on('aborted', common.mustCall());
|
||||
}));
|
||||
}));
|
||||
|
|
Загрузка…
Ссылка в новой задаче