diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js index 6e66ae6d42a9c0..fc34e95bc55ec3 100644 --- a/lib/_http_incoming.js +++ b/lib/_http_incoming.js @@ -2,6 +2,7 @@ const util = require('util'); const Stream = require('stream'); +const StorageObject = require('internal/util').StorageObject; function readStart(socket) { if (socket && !socket._paused && socket.readable) @@ -27,9 +28,9 @@ function IncomingMessage(socket) { this.httpVersionMinor = null; this.httpVersion = null; this.complete = false; - this.headers = {}; + this.headers = new StorageObject(); this.rawHeaders = []; - this.trailers = {}; + this.trailers = new StorageObject(); this.rawTrailers = []; this.readable = true; diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 77bfe4ba77d416..07a5472b1a6d25 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -11,6 +11,7 @@ const common = require('_http_common'); const CRLF = common.CRLF; const chunkExpression = common.chunkExpression; const debug = common.debug; +const StorageObject = internalUtil.StorageObject; const connectionExpression = /^Connection$/i; const transferEncodingExpression = /^Transfer-Encoding$/i; @@ -65,7 +66,7 @@ function OutgoingMessage() { this.shouldKeepAlive = true; this.useChunkedEncodingByDefault = true; this.sendDate = false; - this._removedHeader = {}; + this._removedHeader = new StorageObject(); this._contentLength = null; this._hasBody = true; @@ -78,7 +79,7 @@ function OutgoingMessage() { this.connection = null; this._header = null; this._headers = null; - this._headerNames = {}; + this._headerNames = new StorageObject(); this._onPendingData = null; } @@ -348,7 +349,7 @@ OutgoingMessage.prototype.setHeader = function(name, value) { throw new TypeError('The header content contains invalid characters'); } if (this._headers === null) - this._headers = {}; + this._headers = new StorageObject(); var key = name.toLowerCase(); this._headers[key] = value; @@ -400,9 +401,10 @@ OutgoingMessage.prototype._renderHeaders = function() { } var headersMap = this._headers; - if (!headersMap) return {}; + var headers = new StorageObject(); + + if (!headersMap) return headers; - var headers = {}; var keys = Object.keys(headersMap); var headerNames = this._headerNames; diff --git a/lib/internal/util.js b/lib/internal/util.js index 56398ccf9dc2fe..9e7ca617bf86e8 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -4,6 +4,13 @@ const binding = process.binding('util'); const prefix = `(${process.release.name}:${process.pid}) `; const noDeprecation = process.noDeprecation; +// This constructor is used for creating objects for general storage purposes. +// Instantiating this is faster than explicitly calling `Object.create(null)` +// to get a "clean" empty object (tested with v8 v4.9). +function StorageObject() {} +StorageObject.prototype = Object.create(null); +exports.StorageObject = StorageObject; + exports.getHiddenValue = binding.getHiddenValue; exports.setHiddenValue = binding.setHiddenValue; diff --git a/test/parallel/test-http-header-special-names.js b/test/parallel/test-http-header-special-names.js new file mode 100644 index 00000000000000..5b71bc1473f7b6 --- /dev/null +++ b/test/parallel/test-http-header-special-names.js @@ -0,0 +1,40 @@ +/* eslint-disable no-proto */ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +const clientHeaders = Object.create(null); +const serverHeaders = Object.create(null); +clientHeaders['__proto__'] = serverHeaders['__proto__'] = 'abc'; +clientHeaders['constructor'] = serverHeaders['constructor'] = '123'; +clientHeaders['Foo'] = serverHeaders['Foo'] = '456'; + +const server = http.createServer(common.mustCall((req, res) => { + const headerNames = Object.keys(req.headers); + assert.notStrictEqual(headerNames.indexOf('__proto__'), -1); + assert.notStrictEqual(headerNames.indexOf('constructor'), -1); + assert.notStrictEqual(headerNames.indexOf('foo'), -1); + assert.strictEqual(req.headers['__proto__'], 'abc'); + assert.strictEqual(req.headers['constructor'], '123'); + assert.strictEqual(req.headers['foo'], '456'); + res.writeHead(200, serverHeaders); + res.end(); + server.close(); +})); +server.listen(common.PORT, () => { + http.get({ + port: common.PORT, + headers: clientHeaders + }, common.mustCall((res) => { + const headerNames = Object.keys(res.headers); + assert.equal(res.statusCode, 200); + assert.notStrictEqual(headerNames.indexOf('__proto__'), -1); + assert.notStrictEqual(headerNames.indexOf('constructor'), -1); + assert.notStrictEqual(headerNames.indexOf('foo'), -1); + assert.strictEqual(res.headers['__proto__'], 'abc'); + assert.strictEqual(res.headers['constructor'], '123'); + assert.strictEqual(res.headers['foo'], '456'); + })); +});