Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http: optimize outgoing requests #605

Closed
Closed
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
72 changes: 44 additions & 28 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ exports.OutgoingMessage = OutgoingMessage;
OutgoingMessage.prototype.setTimeout = function(msecs, callback) {
if (callback)
this.on('timeout', callback);

if (!this.socket) {
this.once('socket', function(socket) {
socket.setTimeout(msecs);
Expand Down Expand Up @@ -133,32 +134,36 @@ OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) {
return true;
}

if (this.connection &&
this.connection._httpMessage === this &&
this.connection.writable &&
!this.connection.destroyed) {
var connection = this.connection;
Copy link
Member

Choose a reason for hiding this comment

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

does this optimisation have any measurable impact? just out of interest

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Worst case, it only "saves" one lookup (when connection is falsy); however it's definitely necessary when you get here, when you can be looping a lot of times, one char at a time

edit: this is one of the optimizations that did make the major difference

if (connection &&
connection._httpMessage === this &&
connection.writable &&
!connection.destroyed) {
// There might be pending data in the this.output buffer.
while (this.output.length) {
if (!this.connection.writable) {
this._buffer(data, encoding, callback);
return false;
var outputLength = this.output.length;
if (outputLength > 0) {
var output = this.output;
var outputEncodings = this.outputEncodings;
var outputCallbacks = this.outputCallbacks;
for (var i = 0; i < outputLength; i++) {
connection.write(output[i], outputEncodings[i],
outputCallbacks[i]);
}
var c = this.output.shift();
var e = this.outputEncodings.shift();
var cb = this.outputCallbacks.shift();
this.connection.write(c, e, cb);

this.output = [];
this.outputEncodings = [];
this.outputCallbacks = [];
}

// Directly write to socket.
return this.connection.write(data, encoding, callback);
} else if (this.connection && this.connection.destroyed) {
return connection.write(data, encoding, callback);
} else if (connection && connection.destroyed) {
// The socket was destroyed. If we're still trying to write to it,
// then we haven't gotten the 'close' event yet.
return false;
} else {
// buffer, as long as we're not destroyed.
this._buffer(data, encoding, callback);
return false;
return this._buffer(data, encoding, callback);
}
};

Expand All @@ -183,8 +188,6 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
messageHeader: firstLine
};

var field, value;

if (headers) {
var keys = Object.keys(headers);
var isArray = Array.isArray(headers);
Expand Down Expand Up @@ -365,14 +368,16 @@ OutgoingMessage.prototype._renderHeaders = function() {
throw new Error('Can\'t render headers after they are sent to the client.');
}

if (!this._headers) return {};
var headersMap = this._headers;
if (!headersMap) return {};

var headers = {};
var keys = Object.keys(this._headers);
var keys = Object.keys(headersMap);
var headerNames = this._headerNames;

for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
headers[this._headerNames[key]] = this._headers[key];
headers[headerNames[key]] = headersMap[key];
}
return headers;
};
Expand Down Expand Up @@ -571,13 +576,24 @@ OutgoingMessage.prototype._finish = function() {
// This function, outgoingFlush(), is called by both the Server and Client
// to attempt to flush any pending messages out to the socket.
OutgoingMessage.prototype._flush = function() {
if (this.socket && this.socket.writable) {
var ret;
while (this.output.length) {
var data = this.output.shift();
var encoding = this.outputEncodings.shift();
var cb = this.outputCallbacks.shift();
ret = this.socket.write(data, encoding, cb);
var socket = this.socket;
var outputLength, ret;

if (socket && socket.writable) {
// There might be remaining data in this.output; write it out
outputLength = this.output.length;
if (outputLength > 0) {
var output = this.output;
var outputEncodings = this.outputEncodings;
var outputCallbacks = this.outputCallbacks;
for (var i = 0; i < outputLength; i++) {
ret = socket.write(output[i], outputEncodings[i],
outputCallbacks[i]);
}

this.output = [];
this.outputEncodings = [];
this.outputCallbacks = [];
}

if (this.finished) {
Expand Down