|
@ -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,38 +274,66 @@ 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 { |
|
|
// Buffer
|
|
|
this._buffer(data, encoding); |
|
|
var length = this.output.length; |
|
|
return false; |
|
|
|
|
|
} |
|
|
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]; |
|
|
OutgoingMessage.prototype._buffer = function (data, encoding) { |
|
|
var lastData = this.output[length-1]; |
|
|
// Buffer
|
|
|
|
|
|
if (data.length === 0) return; |
|
|
|
|
|
|
|
|
if ((lastEncoding === encoding) || |
|
|
var length = this.output.length; |
|
|
(!encoding && data.constructor === lastData.constructor)) { |
|
|
|
|
|
this.output[length-1] = lastData + data; |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (length === 0 || typeof data != 'string') { |
|
|
this.output.push(data); |
|
|
this.output.push(data); |
|
|
encoding = encoding || "ascii"; |
|
|
encoding = encoding || "ascii"; |
|
|
this.outputEncodings.push(encoding); |
|
|
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; |
|
|
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 + 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) { |
|
|
|
|
|
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); |
|
|
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) { |
|
|