diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 635e794906..b1e2dff50d 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -38,6 +38,13 @@ var contentLengthExpression = /Content-Length/i; var dateExpression = /Date/i; var expectExpression = /Expect/i; +var automaticHeaders = { + connection: true, + 'content-length': true, + 'transfer-encoding': true, + date: true +}; + var dateCache; function utcDate() { @@ -68,6 +75,7 @@ function OutgoingMessage() { this.shouldKeepAlive = true; this.useChunkedEncodingByDefault = true; this.sendDate = false; + this._removedHeader = {}; this._hasBody = true; this._trailer = ''; @@ -245,7 +253,10 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { } // keep-alive logic - if (state.sentConnectionHeader === false) { + if (this._removedHeader.connection) { + this._last = true; + this.shouldKeepAlive = false; + } else if (state.sentConnectionHeader === false) { var shouldSendKeepAlive = this.shouldKeepAlive && (state.sentContentLengthHeader || this.useChunkedEncodingByDefault || @@ -260,7 +271,7 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { if (state.sentContentLengthHeader == false && state.sentTransferEncodingHeader == false) { - if (this._hasBody) { + if (this._hasBody && !this._removedHeader['transfer-encoding']) { if (this.useChunkedEncodingByDefault) { state.messageHeader += 'Transfer-Encoding: chunked\r\n'; this.chunkedEncoding = true; @@ -325,6 +336,10 @@ OutgoingMessage.prototype.setHeader = function(name, value) { this._headerNames = this._headerNames || {}; this._headers[key] = value; this._headerNames[key] = name; + + if (automaticHeaders[key]) { + this._removedHeader[key] = false; + } }; @@ -349,11 +364,17 @@ OutgoingMessage.prototype.removeHeader = function(name) { throw new Error('Can\'t remove headers after they are sent.'); } - if (!this._headers) return; - var key = name.toLowerCase(); - delete this._headers[key]; - delete this._headerNames[key]; + + if (key === 'date') + this.sendDate = false; + else if (automaticHeaders[key]) + this._removedHeader[key] = true; + + if (this._headers) { + delete this._headers[key]; + delete this._headerNames[key]; + } }; diff --git a/test/simple/test-http-remove-header-stays-removed.js b/test/simple/test-http-remove-header-stays-removed.js new file mode 100644 index 0000000000..a15c7750c5 --- /dev/null +++ b/test/simple/test-http-remove-header-stays-removed.js @@ -0,0 +1,59 @@ +// 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'); + +var server = http.createServer(function(request, response) { + // removed headers should stay removed, even if node automatically adds them + // to the output: + response.removeHeader('connection'); + response.removeHeader('transfer-encoding'); + + // make sure that removing and then setting still works: + response.removeHeader('date'); + response.setHeader('date', 'coffee o clock'); + + response.end('beep boop\n'); + + this.close(); +}); + +var response = ''; + +process.on('exit', function() { + assert.equal('beep boop\n', response); + console.log('ok'); +}); + +server.listen(common.PORT, function() { + http.get({ port: common.PORT }, function(res) { + assert.equal(200, res.statusCode); + assert.deepEqual(res.headers, { date : 'coffee o clock' }); + + res.setEncoding('ascii'); + res.on('data', function(chunk) { + response += chunk; + }); + }); +});