diff --git a/lib/http.js b/lib/http.js index f76bd50bbf..d322ab2553 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;