http: pack response body buffer in first tcp packet
Apply the same optimization to res.end(buf) that is applied to res.end(str). Speeds up `node benchmark/http_simple_auto -k -c 1 -n 25000 buffer/1` (non-chunked response body) by about 750x. That's not a typo. Chunked responses: $ cat tmp/http-chunked-client.js // Run `node benchmark/http_simple` in another terminal. var http = require('http'), url = require('url'); var options = url.parse('http://127.0.0.1:8000/buffer/1/1'); options.agent = new http.Agent({ maxSockets: 1 }); for (var i = 0; i < 25000; ++i) http.get(options); Before: $ time out/Release/node tmp/http-chunked-client.js real 16m40.411s user 0m9.184s sys 0m0.604s After: $ time out/Release/node tmp/http-chunked-client.js real 0m5.386s user 0m2.768s sys 0m0.728s That's still a 185x speed-up. Fixes #4415.
This commit is contained in:
parent
ba407ce410
commit
5a19c07c08
79
lib/http.js
79
lib/http.js
@ -759,6 +759,10 @@ OutgoingMessage.prototype.addTrailers = function(headers) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var zero_chunk_buf = new Buffer('\r\n0\r\n');
|
||||||
|
var crlf_buf = new Buffer('\r\n');
|
||||||
|
|
||||||
|
|
||||||
OutgoingMessage.prototype.end = function(data, encoding) {
|
OutgoingMessage.prototype.end = function(data, encoding) {
|
||||||
if (this.finished) {
|
if (this.finished) {
|
||||||
return false;
|
return false;
|
||||||
@ -776,8 +780,7 @@ OutgoingMessage.prototype.end = function(data, encoding) {
|
|||||||
var ret;
|
var ret;
|
||||||
|
|
||||||
var hot = this._headerSent === false &&
|
var hot = this._headerSent === false &&
|
||||||
typeof(data) === 'string' &&
|
(data && data.length > 0) &&
|
||||||
data.length > 0 &&
|
|
||||||
this.output.length === 0 &&
|
this.output.length === 0 &&
|
||||||
this.connection &&
|
this.connection &&
|
||||||
this.connection.writable &&
|
this.connection.writable &&
|
||||||
@ -789,13 +792,73 @@ OutgoingMessage.prototype.end = function(data, encoding) {
|
|||||||
// res.end(blah);
|
// res.end(blah);
|
||||||
// HACKY.
|
// HACKY.
|
||||||
|
|
||||||
if (this.chunkedEncoding) {
|
if (typeof data === 'string') {
|
||||||
var l = Buffer.byteLength(data, encoding).toString(16);
|
if (this.chunkedEncoding) {
|
||||||
ret = this.connection.write(this._header + l + CRLF +
|
var l = Buffer.byteLength(data, encoding).toString(16);
|
||||||
data + '\r\n0\r\n' +
|
ret = this.connection.write(this._header + l + CRLF +
|
||||||
this._trailer + '\r\n', encoding);
|
data + '\r\n0\r\n' +
|
||||||
|
this._trailer + '\r\n', encoding);
|
||||||
|
} else {
|
||||||
|
ret = this.connection.write(this._header + data, encoding);
|
||||||
|
}
|
||||||
|
} else if (Buffer.isBuffer(data)) {
|
||||||
|
if (this.chunkedEncoding) {
|
||||||
|
var chunk_size = data.length.toString(16);
|
||||||
|
|
||||||
|
// Skip expensive Buffer.byteLength() calls; only ISO-8859-1 characters
|
||||||
|
// are allowed in HTTP headers. Therefore:
|
||||||
|
//
|
||||||
|
// this._header.length == Buffer.byteLength(this._header.length)
|
||||||
|
// this._trailer.length == Buffer.byteLength(this._trailer.length)
|
||||||
|
//
|
||||||
|
var header_len = this._header.length;
|
||||||
|
var chunk_size_len = chunk_size.length;
|
||||||
|
var data_len = data.length;
|
||||||
|
var trailer_len = this._trailer.length;
|
||||||
|
|
||||||
|
var len = header_len
|
||||||
|
+ chunk_size_len
|
||||||
|
+ 2 // '\r\n'.length
|
||||||
|
+ data_len
|
||||||
|
+ 5 // '\r\n0\r\n'.length
|
||||||
|
+ trailer_len
|
||||||
|
+ 2; // '\r\n'.length
|
||||||
|
|
||||||
|
var buf = new Buffer(len);
|
||||||
|
var off = 0;
|
||||||
|
|
||||||
|
buf.write(this._header, off, header_len, 'ascii');
|
||||||
|
off += header_len;
|
||||||
|
|
||||||
|
buf.write(chunk_size, off, chunk_size_len, 'ascii');
|
||||||
|
off += chunk_size_len;
|
||||||
|
|
||||||
|
crlf_buf.copy(buf, off);
|
||||||
|
off += 2;
|
||||||
|
|
||||||
|
data.copy(buf, off);
|
||||||
|
off += data_len;
|
||||||
|
|
||||||
|
zero_chunk_buf.copy(buf, off);
|
||||||
|
off += 5;
|
||||||
|
|
||||||
|
if (trailer_len > 0) {
|
||||||
|
buf.write(this._trailer, off, trailer_len, 'ascii');
|
||||||
|
off += trailer_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
crlf_buf.copy(buf, off);
|
||||||
|
|
||||||
|
ret = this.connection.write(buf);
|
||||||
|
} else {
|
||||||
|
var header_len = this._header.length;
|
||||||
|
var buf = new Buffer(header_len + data.length);
|
||||||
|
buf.write(this._header, 0, header_len, 'ascii');
|
||||||
|
data.copy(buf, header_len);
|
||||||
|
ret = this.connection.write(buf);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = this.connection.write(this._header + data, encoding);
|
throw new TypeError('first argument must be a string or Buffer');
|
||||||
}
|
}
|
||||||
this._headerSent = true;
|
this._headerSent = true;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user