Browse Source

Insert some hot paths into HTTP

v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
80a8e71fe0
  1. 112
      lib/http.js
  2. 2
      test/simple/test-http-304.js

112
lib/http.js

@ -264,9 +264,6 @@ function OutgoingMessage (socket) {
this.shouldKeepAlive = true; this.shouldKeepAlive = true;
this.useChunkedEncodingByDefault = true; this.useChunkedEncodingByDefault = true;
this._headerFlushed = false;
this._header = null; // to be filled by _storeHeader
this._hasBody = true; this._hasBody = true;
this.finished = false; this.finished = false;
@ -277,14 +274,43 @@ exports.OutgoingMessage = OutgoingMessage;
// This abstract either writing directly to the socket or buffering it. // This abstract either writing directly to the socket or buffering it.
// Rename to _writeRaw() ? // Rename to _writeRaw() ?
OutgoingMessage.prototype._send = function (data, encoding) { OutgoingMessage.prototype._send = function (data, encoding) {
if (this.connection._outgoing[0] === this && // This is a shameful hack to get the headers and first body chunk onto
this.connection.writable && // the same packet. Future versions of Node are going to take care of
this.output.length === 0) // 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. // Directly write to socket.
return this.connection.write(data, encoding); return this.connection.write(data, encoding);
} else { } else {
this._buffer(data, encoding);
return false;
}
};
OutgoingMessage.prototype._buffer = function (data, encoding) {
// Buffer // Buffer
if (data.length === 0) return;
var length = this.output.length; var length = this.output.length;
if (length === 0 || typeof data != 'string') { if (length === 0 || typeof data != 'string') {
@ -308,7 +334,6 @@ OutgoingMessage.prototype._send = function (data, encoding) {
this.outputEncodings.push(encoding); this.outputEncodings.push(encoding);
return false; return false;
}
}; };
@ -377,9 +402,8 @@ OutgoingMessage.prototype._storeHeader = function (firstLine, headers) {
} }
} }
messageHeader += CRLF; this._header = messageHeader + CRLF;
this._headerSent = false;
this._header = messageHeader;
// wait until the first body chunk, or close(), is sent to flush. // 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) { OutgoingMessage.prototype.write = function (chunk, encoding) {
if (!this._header) { 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) { 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"); 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; if (chunk.length === 0) return false;
var len, ret; var len, ret;
@ -451,22 +468,50 @@ OutgoingMessage.prototype.close = function (data, encoding) {
OutgoingMessage.prototype.end = function (data, encoding) { OutgoingMessage.prototype.end = function (data, encoding) {
var ret; var ret;
// maybe the header hasn't been sent. if not send it.
if (!this._headerFlushed) { var hot = this._headerSent === false
ret = this._send(this._header); && typeof(data) === "string"
this._headerFlushed = true; && 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;
if (data) { } else if (data) {
// Normal body write.
ret = this.write(data, encoding); ret = this.write(data, encoding);
} }
this.finished = true; if (!hot) {
if (this.chunkedEncoding) { if (this.chunkedEncoding) {
ret = this._send("0\r\n\r\n"); // last chunk 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 // There is the first message on the outgoing queue, and we've sent
// everything to the socket. // everything to the socket.
if (this.output.length === 0 && this.connection._outgoing[0] === this) { 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 // An array of outgoing messages for the socket. In pipelined connections
// we need to keep track of the order they were sent. // we need to keep track of the order they were sent.
socket._outgoing = []; socket._outgoing = [];
socket.__destroyOnDrain = false;
// NOTE: be sure not to use ondrain elsewhere in this file! // NOTE: be sure not to use ondrain elsewhere in this file!
socket.ondrain = function () { socket.ondrain = function () {
var message = socket._outgoing[0]; var message = socket._outgoing[0];
if (message) message.emit('drain'); if (message) message.emit('drain');
if (socket.__destroyOnDrain) socket.destroy();
}; };
} }
@ -740,7 +787,14 @@ function connectionListener (socket) {
var message = socket._outgoing.shift(); var message = socket._outgoing.shift();
if (message._last) { if (message._last) {
// No more messages to be pushed out. // 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) { } else if (socket._outgoing.length) {
// Push out the next message. // Push out the next message.
outgoingFlush(socket); outgoingFlush(socket);
@ -1002,7 +1056,7 @@ exports.cat = function (url, encoding_, headers_) {
client.end(); client.end();
return; return;
} }
res.setBodyEncoding(encoding); res.setEncoding(encoding);
res.addListener('data', function (chunk) { content += chunk; }); res.addListener('data', function (chunk) { content += chunk; });
res.addListener('end', function () { res.addListener('end', function () {
if (callback && !callbackSent) { if (callback && !callbackSent) {

2
test/simple/test-http-304.js

@ -7,7 +7,7 @@ var sys = require('sys'),
s = http.createServer(function (request, response) { s = http.createServer(function (request, response) {
response.writeHead(304); response.writeHead(304);
response.end(); response.end();
}) });
s.listen(PORT); s.listen(PORT);
sys.puts('Server running at http://127.0.0.1:'+PORT+'/') sys.puts('Server running at http://127.0.0.1:'+PORT+'/')

Loading…
Cancel
Save