diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2012-12-19 12:35:07 +0100 |
---|---|---|
committer | Ben Noordhuis <info@bnoordhuis.nl> | 2012-12-20 12:02:06 +0100 |
commit | 5a19c07c087c5749e31737cb4fb5a113643f35d6 (patch) | |
tree | 365ae700abb310dba417e2ad673f6bc7a428b814 | |
parent | ba407ce4106635949fcd5fdeced255d543ab1079 (diff) | |
download | node-5a19c07c087c5749e31737cb4fb5a113643f35d6.tar.gz |
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.
-rw-r--r-- | lib/http.js | 79 |
1 files changed, 71 insertions, 8 deletions
diff --git a/lib/http.js b/lib/http.js index f76bd50bb..d322ab255 100644 --- a/lib/http.js +++ b/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) { if (this.finished) { return false; @@ -776,8 +780,7 @@ OutgoingMessage.prototype.end = function(data, encoding) { var ret; var hot = this._headerSent === false && - typeof(data) === 'string' && - data.length > 0 && + (data && data.length > 0) && this.output.length === 0 && this.connection && this.connection.writable && @@ -789,13 +792,73 @@ OutgoingMessage.prototype.end = function(data, encoding) { // res.end(blah); // HACKY. - if (this.chunkedEncoding) { - var l = Buffer.byteLength(data, encoding).toString(16); - ret = this.connection.write(this._header + l + CRLF + - data + '\r\n0\r\n' + - this._trailer + '\r\n', encoding); + if (typeof data === 'string') { + if (this.chunkedEncoding) { + var l = Buffer.byteLength(data, encoding).toString(16); + ret = this.connection.write(this._header + l + CRLF + + 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 { - ret = this.connection.write(this._header + data, encoding); + throw new TypeError('first argument must be a string or Buffer'); } this._headerSent = true; |