const contentLengthExpression = /^Content-Length$/i;
const dateExpression = /^Date$/i;
const expectExpression = /^Expect$/i;
+const trailerExpression = /^Trailer$/i;
const automaticHeaders = {
connection: true,
this.sendDate = false;
this._removedHeader = {};
+ this._contentLength = null;
this._hasBody = true;
this._trailer = '';
sentTransferEncodingHeader: false,
sentDateHeader: false,
sentExpect: false,
+ sentTrailer: false,
messageHeader: firstLine
};
if (state.sentContentLengthHeader === false &&
state.sentTransferEncodingHeader === false) {
- if (this._hasBody && !this._removedHeader['transfer-encoding']) {
- if (this.useChunkedEncodingByDefault) {
+ if (!this._hasBody) {
+ // Make sure we don't end the 0\r\n\r\n at the end of the message.
+ this.chunkedEncoding = false;
+ } else if (!this.useChunkedEncodingByDefault) {
+ this._last = true;
+ } else {
+ if (!state.sentTrailer &&
+ !this._removedHeader['content-length'] &&
+ typeof this._contentLength === 'number') {
+ state.messageHeader += 'Content-Length: ' + this._contentLength +
+ '\r\n';
+ } else if (!this._removedHeader['transfer-encoding']) {
state.messageHeader += 'Transfer-Encoding: chunked\r\n';
this.chunkedEncoding = true;
} else {
- this._last = true;
+ // We should only be able to get here if both Content-Length and
+ // Transfer-Encoding are removed by the user.
+ // See: test/parallel/test-http-remove-header-stays-removed.js
+ debug('Both Content-Length and Transfer-Encoding are removed');
}
- } else {
- // Make sure we don't end the 0\r\n\r\n at the end of the message.
- this.chunkedEncoding = false;
}
}
state.sentDateHeader = true;
} else if (expectExpression.test(field)) {
state.sentExpect = true;
+ } else if (trailerExpression.test(field)) {
+ state.sentTrailer = true;
}
}
this.once('finish', callback);
if (!this._header) {
+ if (data) {
+ if (typeof data === 'string')
+ this._contentLength = Buffer.byteLength(data, encoding);
+ else
+ this._contentLength = data.length;
+ } else {
+ this._contentLength = 0;
+ }
this._implicitHeader();
}
var server = http.createServer(function(req, res) {
res.setHeader('X-Date', 'foo');
res.setHeader('X-Connection', 'bar');
- res.setHeader('X-Transfer-Encoding', 'baz');
+ res.setHeader('X-Content-Length', 'baz');
res.end();
});
server.listen(common.PORT);
assert.equal(res.statusCode, 200);
assert.equal(res.headers['x-date'], 'foo');
assert.equal(res.headers['x-connection'], 'bar');
- assert.equal(res.headers['x-transfer-encoding'], 'baz');
+ assert.equal(res.headers['x-content-length'], 'baz');
assert(res.headers['date']);
assert.equal(res.headers['connection'], 'keep-alive');
- assert.equal(res.headers['transfer-encoding'], 'chunked');
+ assert.equal(res.headers['content-length'], '0');
server.close();
agent.destroy();
});
'GET': ['host', 'connection'],
'HEAD': ['host', 'connection'],
'OPTIONS': ['host', 'connection'],
- 'POST': ['host', 'connection', 'transfer-encoding'],
- 'PUT': ['host', 'connection', 'transfer-encoding']
+ 'POST': ['host', 'connection', 'content-length'],
+ 'PUT': ['host', 'connection', 'content-length']
};
var expectedMethods = Object.keys(expectedHeaders);
--- /dev/null
+var common = require('../common');
+var assert = require('assert');
+var http = require('http');
+
+var expectedHeadersMultipleWrites = {
+ 'connection': 'close',
+ 'transfer-encoding': 'chunked',
+};
+
+var expectedHeadersEndWithData = {
+ 'connection': 'close',
+ 'content-length': 'hello world'.length,
+};
+
+var expectedHeadersEndNoData = {
+ 'connection': 'close',
+ 'content-length': '0',
+};
+
+var receivedRequests = 0;
+var totalRequests = 2;
+
+var server = http.createServer(function(req, res) {
+ res.removeHeader('Date');
+
+ switch (req.url.substr(1)) {
+ case 'multiple-writes':
+ assert.deepEqual(req.headers, expectedHeadersMultipleWrites);
+ res.write('hello');
+ res.end('world');
+ break;
+ case 'end-with-data':
+ assert.deepEqual(req.headers, expectedHeadersEndWithData);
+ res.end('hello world');
+ break;
+ case 'empty':
+ assert.deepEqual(req.headers, expectedHeadersEndNoData);
+ res.end();
+ break;
+ default:
+ throw new Error('Unreachable');
+ break;
+ }
+
+ receivedRequests++;
+ if (totalRequests === receivedRequests) server.close();
+});
+
+server.listen(common.PORT, function() {
+ var req;
+
+ req = http.request({
+ port: common.PORT,
+ method: 'POST',
+ path: '/multiple-writes'
+ });
+ req.removeHeader('Date');
+ req.removeHeader('Host');
+ req.write('hello ');
+ req.end('world');
+ req.on('response', function(res) {
+ assert.deepEqual(res.headers, expectedHeadersMultipleWrites);
+ });
+
+ req = http.request({
+ port: common.PORT,
+ method: 'POST',
+ path: '/end-with-data'
+ });
+ req.removeHeader('Date');
+ req.removeHeader('Host');
+ req.end('hello world');
+ req.on('response', function(res) {
+ assert.deepEqual(res.headers, expectedHeadersEndWithData);
+ });
+
+ req = http.request({
+ port: common.PORT,
+ method: 'POST',
+ path: '/empty'
+ });
+ req.removeHeader('Date');
+ req.removeHeader('Host');
+ req.end();
+ req.on('response', function(res) {
+ assert.deepEqual(res.headers, expectedHeadersEndNoData);
+ });
+
+});
});
req.resume();
+ res.setHeader('Trailer', 'x-foo');
res.addTrailers([
['x-fOo', 'xOxOxOx'],
['x-foO', 'OxOxOxO'],
req.end('y b a r');
req.on('response', function(res) {
var expectRawHeaders = [
+ 'Trailer',
+ 'x-foo',
'Date',
null,
'Connection',
'chunked'
];
var expectHeaders = {
+ trailer: 'x-foo',
date: null,
connection: 'close',
'transfer-encoding': 'chunked'
};
- res.rawHeaders[1] = null;
+ res.rawHeaders[3] = null;
res.headers.date = null;
assert.deepEqual(res.rawHeaders, expectRawHeaders);
assert.deepEqual(res.headers, expectHeaders);
// to the output:
response.removeHeader('connection');
response.removeHeader('transfer-encoding');
+ response.removeHeader('content-length');
// make sure that removing and then setting still works:
response.removeHeader('date');