mirror of https://github.com/nodejs/node.git
Insert some hot paths into HTTP
This commit is contained in:
parent
74b7fa29a1
commit
80a8e71fe0
148
lib/http.js
148
lib/http.js
|
@ -264,9 +264,6 @@ function OutgoingMessage (socket) {
|
|||
this.shouldKeepAlive = true;
|
||||
this.useChunkedEncodingByDefault = true;
|
||||
|
||||
this._headerFlushed = false;
|
||||
this._header = null; // to be filled by _storeHeader
|
||||
|
||||
this._hasBody = true;
|
||||
|
||||
this.finished = false;
|
||||
|
@ -277,38 +274,66 @@ exports.OutgoingMessage = OutgoingMessage;
|
|||
// This abstract either writing directly to the socket or buffering it.
|
||||
// Rename to _writeRaw() ?
|
||||
OutgoingMessage.prototype._send = function (data, encoding) {
|
||||
if (this.connection._outgoing[0] === this &&
|
||||
this.connection.writable &&
|
||||
this.output.length === 0)
|
||||
{
|
||||
// This is a shameful hack to get the headers and first body chunk onto
|
||||
// the same packet. Future versions of Node are going to take care of
|
||||
// this at a lower level and in a more general way.
|
||||
if (!this._headerSent) {
|
||||
if (typeof data === 'string') {
|
||||
data = this._header + data;
|
||||
} else {
|
||||
this.output.unshift(this._header);
|
||||
this.outputEncodings.unshift('ascii');
|
||||
}
|
||||
this._headerSent = true;
|
||||
}
|
||||
|
||||
if (this.connection._outgoing[0] === this && this.connection.writable) {
|
||||
// There might be pending data in the this.output buffer.
|
||||
while (this.output.length) {
|
||||
if (!this.connection.writable) {
|
||||
this._buffer(data, encoding);
|
||||
return false;
|
||||
}
|
||||
var c = this.output.shift();
|
||||
var e = this.outputEncodings.shift();
|
||||
this.connection.write(c, e);
|
||||
}
|
||||
|
||||
// Directly write to socket.
|
||||
return this.connection.write(data, encoding);
|
||||
} else {
|
||||
// Buffer
|
||||
var length = this.output.length;
|
||||
this._buffer(data, encoding);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
if (length === 0 || typeof data != 'string') {
|
||||
this.output.push(data);
|
||||
encoding = encoding || "ascii";
|
||||
this.outputEncodings.push(encoding);
|
||||
return false;
|
||||
}
|
||||
OutgoingMessage.prototype._buffer = function (data, encoding) {
|
||||
// Buffer
|
||||
if (data.length === 0) return;
|
||||
|
||||
var lastEncoding = this.outputEncodings[length-1];
|
||||
var lastData = this.output[length-1];
|
||||
|
||||
if ((lastEncoding === encoding) ||
|
||||
(!encoding && data.constructor === lastData.constructor)) {
|
||||
this.output[length-1] = lastData + data;
|
||||
return false;
|
||||
}
|
||||
var length = this.output.length;
|
||||
|
||||
if (length === 0 || typeof data != 'string') {
|
||||
this.output.push(data);
|
||||
encoding = encoding || "ascii";
|
||||
this.outputEncodings.push(encoding);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var lastEncoding = this.outputEncodings[length-1];
|
||||
var lastData = this.output[length-1];
|
||||
|
||||
if ((lastEncoding === encoding) ||
|
||||
(!encoding && data.constructor === lastData.constructor)) {
|
||||
this.output[length-1] = lastData + data;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.output.push(data);
|
||||
encoding = encoding || "ascii";
|
||||
this.outputEncodings.push(encoding);
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
|
@ -377,9 +402,8 @@ OutgoingMessage.prototype._storeHeader = function (firstLine, headers) {
|
|||
}
|
||||
}
|
||||
|
||||
messageHeader += CRLF;
|
||||
|
||||
this._header = messageHeader;
|
||||
this._header = messageHeader + CRLF;
|
||||
this._headerSent = false;
|
||||
// wait until the first body chunk, or close(), is sent to flush.
|
||||
};
|
||||
|
||||
|
@ -391,7 +415,7 @@ OutgoingMessage.prototype.sendBody = function () {
|
|||
|
||||
OutgoingMessage.prototype.write = function (chunk, encoding) {
|
||||
if (!this._header) {
|
||||
throw new Error("writeHead() must be called before write()")
|
||||
throw new Error("You have to call writeHead() before write()");
|
||||
}
|
||||
|
||||
if (!this._hasBody) {
|
||||
|
@ -404,13 +428,6 @@ OutgoingMessage.prototype.write = function (chunk, encoding) {
|
|||
throw new TypeError("first argument must be a string, Array, or Buffer");
|
||||
}
|
||||
|
||||
// write the header
|
||||
|
||||
if (!this._headerFlushed) {
|
||||
this._send(this._header);
|
||||
this._headerFlushed = true;
|
||||
}
|
||||
|
||||
if (chunk.length === 0) return false;
|
||||
|
||||
var len, ret;
|
||||
|
@ -451,22 +468,50 @@ OutgoingMessage.prototype.close = function (data, encoding) {
|
|||
|
||||
OutgoingMessage.prototype.end = function (data, encoding) {
|
||||
var ret;
|
||||
// maybe the header hasn't been sent. if not send it.
|
||||
if (!this._headerFlushed) {
|
||||
ret = this._send(this._header);
|
||||
this._headerFlushed = true;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
var hot = this._headerSent === false
|
||||
&& typeof(data) === "string"
|
||||
&& data.length > 0
|
||||
&& this.output.length === 0
|
||||
&& this.connection.writable
|
||||
&& this.connection._outgoing[0] === this
|
||||
;
|
||||
|
||||
if (hot) {
|
||||
// Hot path. They're doing
|
||||
// res.writeHead();
|
||||
// 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\r\n"
|
||||
, encoding
|
||||
);
|
||||
} else {
|
||||
ret = this.connection.write(this._header + data, encoding);
|
||||
}
|
||||
this._headerSent = true;
|
||||
|
||||
} else if (data) {
|
||||
// Normal body write.
|
||||
ret = this.write(data, encoding);
|
||||
}
|
||||
|
||||
this.finished = true;
|
||||
|
||||
if (this.chunkedEncoding) {
|
||||
ret = this._send("0\r\n\r\n"); // last chunk
|
||||
if (!hot) {
|
||||
if (this.chunkedEncoding) {
|
||||
ret = this._send('0\r\n\r\n'); // Last chunk.
|
||||
} else if (!data) {
|
||||
// Force a flush, HACK.
|
||||
ret = this._send('');
|
||||
}
|
||||
}
|
||||
|
||||
this.finished = true;
|
||||
|
||||
// There is the first message on the outgoing queue, and we've sent
|
||||
// everything to the socket.
|
||||
if (this.output.length === 0 && this.connection._outgoing[0] === this) {
|
||||
|
@ -638,11 +683,13 @@ function httpSocketSetup (socket) {
|
|||
// An array of outgoing messages for the socket. In pipelined connections
|
||||
// we need to keep track of the order they were sent.
|
||||
socket._outgoing = [];
|
||||
socket.__destroyOnDrain = false;
|
||||
|
||||
// NOTE: be sure not to use ondrain elsewhere in this file!
|
||||
socket.ondrain = function () {
|
||||
var message = socket._outgoing[0];
|
||||
if (message) message.emit('drain');
|
||||
if (socket.__destroyOnDrain) socket.destroy();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -740,7 +787,14 @@ function connectionListener (socket) {
|
|||
var message = socket._outgoing.shift();
|
||||
if (message._last) {
|
||||
// No more messages to be pushed out.
|
||||
socket.end();
|
||||
|
||||
// HACK: need way to do this with socket interface
|
||||
if (socket._writeQueue.length) {
|
||||
socket.__destroyOnDrain = true; //socket.end();
|
||||
} else {
|
||||
socket.destroy();
|
||||
}
|
||||
|
||||
} else if (socket._outgoing.length) {
|
||||
// Push out the next message.
|
||||
outgoingFlush(socket);
|
||||
|
@ -1002,7 +1056,7 @@ exports.cat = function (url, encoding_, headers_) {
|
|||
client.end();
|
||||
return;
|
||||
}
|
||||
res.setBodyEncoding(encoding);
|
||||
res.setEncoding(encoding);
|
||||
res.addListener('data', function (chunk) { content += chunk; });
|
||||
res.addListener('end', function () {
|
||||
if (callback && !callbackSent) {
|
||||
|
|
|
@ -7,7 +7,7 @@ var sys = require('sys'),
|
|||
s = http.createServer(function (request, response) {
|
||||
response.writeHead(304);
|
||||
response.end();
|
||||
})
|
||||
});
|
||||
s.listen(PORT);
|
||||
sys.puts('Server running at http://127.0.0.1:'+PORT+'/')
|
||||
|
||||
|
|
Loading…
Reference in New Issue