-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
http: improve errors thrown in header validation #16715
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -818,9 +818,11 @@ HTTP/1 connection specific headers are forbidden to be used in HTTP/2 | |
requests and responses. | ||
|
||
<a id="ERR_HTTP2_INVALID_HEADER_VALUE"></a> | ||
### ERR_HTTP2_INVALID_HEADER_VALUE | ||
### ERR_HTTP_INVALID_HEADER_VALUE | ||
<a id="ERR_HTTP_INVALID_HEADER_VALUE"></a> | ||
### ERR_HTTP_INVALID_HEADER_VALUE | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Accidentally doubled here? |
||
|
||
Used to indicate that an invalid HTTP/2 header value has been specified. | ||
Used to indicate that an invalid HTTP header value has been specified. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we explicitly say HTTP/1.1 and HTTP/2? |
||
|
||
<a id="ERR_HTTP2_INVALID_INFO_STATUS"></a> | ||
### ERR_HTTP2_INVALID_INFO_STATUS | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -437,16 +437,7 @@ function _storeHeader(firstLine, headers) { | |
|
||
function storeHeader(self, state, key, value, validate) { | ||
if (validate) { | ||
if (typeof key !== 'string' || !key || !checkIsHttpToken(key)) { | ||
throw new errors.TypeError( | ||
'ERR_INVALID_HTTP_TOKEN', 'Header name', key); | ||
} | ||
if (value === undefined) { | ||
throw new errors.TypeError('ERR_MISSING_ARGS', `header "${key}"`); | ||
} else if (checkInvalidHeaderChar(value)) { | ||
debug('Header "%s" contains invalid characters', key); | ||
throw new errors.TypeError('ERR_INVALID_CHAR', 'header content', key); | ||
} | ||
validateHeader(key, value); | ||
} | ||
state.header += key + ': ' + escapeHeaderValue(value) + CRLF; | ||
matchHeader(self, state, key, value); | ||
|
@@ -494,20 +485,27 @@ function matchHeader(self, state, field, value) { | |
} | ||
} | ||
|
||
function validateHeader(msg, name, value) { | ||
if (typeof name !== 'string' || !name || !checkIsHttpToken(name)) | ||
throw new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Header name', name); | ||
if (value === undefined) | ||
throw new errors.TypeError('ERR_MISSING_ARGS', 'value'); | ||
if (msg._header) | ||
throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'set'); | ||
if (checkInvalidHeaderChar(value)) { | ||
function validateHeader(name, value) { | ||
var err; | ||
if (typeof name !== 'string' || !name || !checkIsHttpToken(name)) { | ||
err = new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Header name', name); | ||
} else if (value === undefined) { | ||
err = new errors.TypeError('ERR_HTTP_INVALID_HEADER_VALUE', value, name); | ||
} else if (checkInvalidHeaderChar(value)) { | ||
debug('Header "%s" contains invalid characters', name); | ||
throw new errors.TypeError('ERR_INVALID_CHAR', 'header content', name); | ||
err = new errors.TypeError('ERR_INVALID_CHAR', 'header content', name); | ||
} | ||
if (err) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is an object, can we do a strict comparison to |
||
Error.captureStackTrace(err, validateHeader); | ||
throw err; | ||
} | ||
} | ||
|
||
OutgoingMessage.prototype.setHeader = function setHeader(name, value) { | ||
validateHeader(this, name, value); | ||
if (this._header) { | ||
throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'set'); | ||
} | ||
validateHeader(name, value); | ||
|
||
if (!this[outHeadersKey]) | ||
this[outHeadersKey] = {}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,12 +40,18 @@ let statusMessageWarned = false; | |
// close as possible to the current require('http') API | ||
|
||
function assertValidHeader(name, value) { | ||
if (name === '' || typeof name !== 'string') | ||
throw new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Header name', name); | ||
if (isPseudoHeader(name)) | ||
throw new errors.Error('ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED'); | ||
if (value === undefined || value === null) | ||
throw new errors.TypeError('ERR_HTTP2_INVALID_HEADER_VALUE'); | ||
var err; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: can we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am still under the impression that we favor |
||
if (name === '' || typeof name !== 'string') { | ||
err = new errors.TypeError('ERR_INVALID_HTTP_TOKEN', 'Header name', name); | ||
} else if (isPseudoHeader(name)) { | ||
err = new errors.Error('ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED'); | ||
} else if (value === undefined || value === null) { | ||
err = new errors.TypeError('ERR_HTTP_INVALID_HEADER_VALUE', value, name); | ||
} | ||
if (err) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as for |
||
Error.captureStackTrace(err, assertValidHeader); | ||
throw err; | ||
} | ||
} | ||
|
||
function isPseudoHeader(name) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,17 +26,17 @@ server.listen(0, common.mustCall(() => { | |
common.expectsError( | ||
() => response.setTrailer('test', undefined), | ||
{ | ||
code: 'ERR_HTTP2_INVALID_HEADER_VALUE', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not 100% sure about this because the semantics of the http2 validation are slightly different, for example pseudo headers are an http2 only feature. |
||
code: 'ERR_HTTP_INVALID_HEADER_VALUE', | ||
type: TypeError, | ||
message: 'Value must not be undefined or null' | ||
message: 'Invalid value "undefined" for header "test"' | ||
} | ||
); | ||
common.expectsError( | ||
() => response.setTrailer('test', null), | ||
{ | ||
code: 'ERR_HTTP2_INVALID_HEADER_VALUE', | ||
code: 'ERR_HTTP_INVALID_HEADER_VALUE', | ||
type: TypeError, | ||
message: 'Value must not be undefined or null' | ||
message: 'Invalid value "null" for header "test"' | ||
} | ||
); | ||
common.expectsError( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is duplicated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it's accidentally changed..we need to keep the documentation for
ERR_HTTP2_INVALID_HEADER_VALUE
because it's out there already.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the codebase doesn't raise an error with this code though, should it be removed? 😬