diff --git a/lib/_http_common.js b/lib/_http_common.js index 0d55651da1b..297ac8f063c 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -87,12 +87,7 @@ function parserOnHeadersComplete(info) { n = Math.min(n, parser.maxHeaderPairs); } - for (var i = 0; i < n; i += 2) { - var k = headers[i]; - var v = headers[i + 1]; - parser.incoming._addHeaderLine(k, v); - } - + parser.incoming._addHeaderLines(headers, n); if (info.method) { // server only @@ -147,11 +142,7 @@ function parserOnMessageComplete() { // Emit any trailing headers. var headers = parser._headers; if (headers) { - for (var i = 0, n = headers.length; i < n; i += 2) { - var k = headers[i]; - var v = headers[i + 1]; - parser.incoming._addHeaderLine(k, v); - } + parser.incoming._addHeaderLines(headers, headers.length); parser._headers = []; parser._url = ''; } diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js index f609e4fc21d..8949d4571ae 100644 --- a/lib/_http_incoming.js +++ b/lib/_http_incoming.js @@ -49,7 +49,9 @@ function IncomingMessage(socket) { this.httpVersion = null; this.complete = false; this.headers = {}; + this.rawHeaders = []; this.trailers = {}; + this.rawTrailers = []; this.readable = true; @@ -111,6 +113,27 @@ IncomingMessage.prototype.destroy = function(error) { }; +IncomingMessage.prototype._addHeaderLines = function(headers, n) { + if (headers && headers.length) { + var raw, dest; + if (this.complete) { + raw = this.rawTrailers; + dest = this.trailers; + } else { + raw = this.rawHeaders; + dest = this.headers; + } + raw.push.apply(raw, headers); + + for (var i = 0; i < n; i += 2) { + var k = headers[i]; + var v = headers[i + 1]; + this._addHeaderLine(k, v, dest); + } + } +}; + + // Add the given (field, value) pair to the message // // Per RFC2616, section 4.2 it is acceptable to join multiple instances of the @@ -118,9 +141,7 @@ IncomingMessage.prototype.destroy = function(error) { // multiple values this way. If not, we declare the first instance the winner // and drop the second. Extended header fields (those beginning with 'x-') are // always joined. -IncomingMessage.prototype._addHeaderLine = function(field, value) { - var dest = this.complete ? this.trailers : this.headers; - +IncomingMessage.prototype._addHeaderLine = function(field, value, dest) { field = field.toLowerCase(); switch (field) { // Array headers: diff --git a/test/simple/test-http-raw-headers.js b/test/simple/test-http-raw-headers.js new file mode 100644 index 00000000000..bb34b9dce2c --- /dev/null +++ b/test/simple/test-http-raw-headers.js @@ -0,0 +1,131 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var http = require('http'); + +http.createServer(function(req, res) { + this.close(); + var expectRawHeaders = [ + 'Host', + 'localhost:12346', + 'transfer-ENCODING', + 'CHUNKED', + 'x-BaR', + 'yoyoyo', + 'Connection', + 'keep-alive' + ]; + var expectHeaders = { + host: 'localhost:12346', + 'transfer-encoding': 'CHUNKED', + 'x-bar': 'yoyoyo', + connection: 'keep-alive' + }; + + var expectRawTrailers = [ + 'x-bAr', + 'yOyOyOy', + 'x-baR', + 'OyOyOyO', + 'X-bAr', + 'yOyOyOy', + 'X-baR', + 'OyOyOyO' + ]; + + var expectTrailers = { 'x-bar': 'yOyOyOy, OyOyOyO, yOyOyOy, OyOyOyO' }; + + assert.deepEqual(req.rawHeaders, expectRawHeaders); + assert.deepEqual(req.headers, expectHeaders); + + req.on('end', function() { + assert.deepEqual(req.rawTrailers, expectRawTrailers); + assert.deepEqual(req.trailers, expectTrailers); + }); + + req.resume(); + res.addTrailers([ + ['x-fOo', 'xOxOxOx'], + ['x-foO', 'OxOxOxO'], + ['X-fOo', 'xOxOxOx'], + ['X-foO', 'OxOxOxO'] + ]); + res.end('x f o o'); +}).listen(common.PORT, function() { + var expectRawHeaders = [ + 'Date', + 'Tue, 06 Aug 2013 01:31:54 GMT', + 'Connection', + 'keep-alive', + 'Transfer-Encoding', + 'chunked' + ]; + var req = http.request({ port: common.PORT, path: '/' }); + req.addTrailers([ + ['x-bAr', 'yOyOyOy'], + ['x-baR', 'OyOyOyO'], + ['X-bAr', 'yOyOyOy'], + ['X-baR', 'OyOyOyO'] + ]); + req.setHeader('transfer-ENCODING', 'CHUNKED'); + req.setHeader('x-BaR', 'yoyoyo'); + req.end('y b a r'); + req.on('response', function(res) { + var expectRawHeaders = [ + 'Date', + null, + 'Connection', + 'keep-alive', + 'Transfer-Encoding', + 'chunked' + ]; + var expectHeaders = { + date: null, + connection: 'keep-alive', + 'transfer-encoding': 'chunked' + }; + res.rawHeaders[1] = null; + res.headers.date = null; + assert.deepEqual(res.rawHeaders, expectRawHeaders); + assert.deepEqual(res.headers, expectHeaders); + res.on('end', function() { + var expectRawTrailers = [ + 'x-fOo', + 'xOxOxOx', + 'x-foO', + 'OxOxOxO', + 'X-fOo', + 'xOxOxOx', + 'X-foO', + 'OxOxOxO' + ]; + var expectTrailers = { 'x-foo': 'xOxOxOx, OxOxOxO, xOxOxOx, OxOxOxO' }; + + assert.deepEqual(res.rawTrailers, expectRawTrailers); + assert.deepEqual(res.trailers, expectTrailers); + console.log('ok'); + }); + res.resume(); + }); +});