- Version: v8.11.1
- Platform: Win 64-bit
- Subsystem:
As it is documented, response.writeHead method should be called only once.
When the response.writeHead method is called twice, an error ("Can't set headers after they are sent.") is thrown if response.setHeader method was called. And the error is not thrown if the response.setHeader method was not called.
For example:
Run this code as a server:
'use strict';
require('http').createServer((request, response) => {
request.addListener('end', () => {
console.log('Call: writeHead #1');
response.writeHead(200, {
'Content-Type': 'text/plain',
'X-COUNT': 1
});
console.log('Call: writeHead #2');
response.writeHead(200, {
'Content-Type': 'text/plain',
'X-COUNT': 2
});
response.end('Hello, World!');
}).resume();
}).listen(8080);
And run this code as a client (using request module):
'use strict';
require('request')({
url: 'http://localhost:8080/'
}, (error, response, body) => {
console.log('error: ', error);
console.log('statusCode: ', response && response.statusCode);
console.log('headers:');
console.log(response && response.headers);
console.log('body: ', body);
});
That works fine without an error but the code as server calls the response.writeHead method twice.
So, run this code as a server instead:
'use strict';
require('http').createServer((request, response) => {
request.addListener('end', () => {
console.log('Call: setHeader');
response.setHeader('foo', 'bar');
console.log('Call: writeHead #1');
response.writeHead(200, {
'Content-Type': 'text/plain',
'X-COUNT': 1
});
console.log('Call: writeHead #2');
response.writeHead(200, {
'Content-Type': 'text/plain',
'X-COUNT': 2
});
response.end('Hello, World!');
}).resume();
}).listen(8080);
And run the client above.
Then, an error "Can't set headers after they are sent." is thrown.
Just only the response.setHeader method was added to the second server.
Is this correct behavior?
Of course the response.writeHead method should not be called again. However I feel strange about that it is checked only when the response.setHeader method was called.
It seems that this issue is relevant to #8446, maybe. I take notice of an error that is thrown by the response.setHeader method.
In OutgoingMessage.prototype.setHeader, the error is thrown if the headers are already sent (rendered).
|
throw new ERR_HTTP_HEADERS_SENT('set'); |
In
ServerResponse.prototype.writeHead, the
OutgoingMessage.prototype.setHeader is called only when current instance has header data (i.e. it has
this[outHeadersKey]).
|
if (this[outHeadersKey]) { |
In other words, the
response.setHeader method that checks duplicated calling is called only when that method was already called (i.e.
this[outHeadersKey] was already set).
Also, duplicated calling is checked after headers were merged.
|
if (k === undefined && this._header) { |
I think that the checking should be moved to after if ... else.
// only progressive api is used
headers = this[outHeadersKey];
} else {
// only writeHead() called
headers = obj;
}
if (this._header) {
throw new ERR_HTTP_HEADERS_SENT('render');
}
That is, duplicated calling should be checked whether headers were merged or not.
Or, checking at top of writeHead is better because it has to do nothing when it throws an error.
function writeHead(statusCode, reason, obj) {
if (this._header) {
throw new ERR_HTTP_HEADERS_SENT('render');
}
var originalStatusCode = statusCode;
And also, it seems that there is another problem in that checking.
Run this code as a server instead:
'use strict';
require('http').createServer((request, response) => {
request.addListener('end', () => {
console.log('Call: setHeader');
response.setHeader('foo', 'bar');
console.log('Call: writeHead #1');
response.writeHead(200, {
'Content-Type': 'text/plain',
'X-COUNT': 1
});
console.log('Call: writeHead #2');
response.writeHead(200, {
'': 'head'
});
response.end('Hello, World!');
}).resume();
}).listen(8080);
This is the same as the code that throws an error except for last header name. However this does not throw an error.
After header were merged, duplicated calling is checked only when the last header name is undefined.
|
if (k === undefined && this._header) { |
A value of the
k was updated repeatedly, and it has last header name or initial value. And duplicated calling is checked by the
response.setHeader method in a loop only when the
k is truthy.
If the
k === undefined means that the
response.setHeader method never be called, that was not sufficient.
I think that checking the
k is not needed (i.e.
if (this._header) {) because duplicated calling can be checked even if that was already checked in the
response.setHeader method.
This problem also is solved by changing above, as I indicated.
As it is documented,
response.writeHeadmethod should be called only once.When the
response.writeHeadmethod is called twice, an error ("Can't set headers after they are sent.") is thrown ifresponse.setHeadermethod was called. And the error is not thrown if theresponse.setHeadermethod was not called.For example:
Run this code as a server:
And run this code as a client (using request module):
That works fine without an error but the code as server calls the
response.writeHeadmethod twice.So, run this code as a server instead:
And run the client above.
Then, an error "Can't set headers after they are sent." is thrown.
Just only the
response.setHeadermethod was added to the second server.Is this correct behavior?
Of course the
response.writeHeadmethod should not be called again. However I feel strange about that it is checked only when theresponse.setHeadermethod was called.It seems that this issue is relevant to #8446, maybe. I take notice of an error that is thrown by the
response.setHeadermethod.In
OutgoingMessage.prototype.setHeader, the error is thrown if the headers are already sent (rendered).node/lib/_http_outgoing.js
Line 471 in 9a02de7
In
ServerResponse.prototype.writeHead, theOutgoingMessage.prototype.setHeaderis called only when current instance has header data (i.e. it hasthis[outHeadersKey]).node/lib/_http_server.js
Line 224 in 9a02de7
In other words, the
response.setHeadermethod that checks duplicated calling is called only when that method was already called (i.e.this[outHeadersKey]was already set).Also, duplicated calling is checked after headers were merged.
node/lib/_http_server.js
Line 234 in 9a02de7
I think that the checking should be moved to after
if ... else.That is, duplicated calling should be checked whether headers were merged or not.
Or, checking at top of
writeHeadis better because it has to do nothing when it throws an error.And also, it seems that there is another problem in that checking.
Run this code as a server instead:
This is the same as the code that throws an error except for last header name. However this does not throw an error.
After header were merged, duplicated calling is checked only when the last header name is
undefined.node/lib/_http_server.js
Line 234 in 9a02de7
A value of the
kwas updated repeatedly, and it has last header name or initial value. And duplicated calling is checked by theresponse.setHeadermethod in a loop only when thekis truthy.If the
k === undefinedmeans that theresponse.setHeadermethod never be called, that was not sufficient.I think that checking the
kis not needed (i.e.if (this._header) {) because duplicated calling can be checked even if that was already checked in theresponse.setHeadermethod.This problem also is solved by changing above, as I indicated.