-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
http2: allow Host in HTTP/2 requests #34664
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
Changes from 2 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 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2,6 +2,10 @@ | |||||||||||||
<!-- YAML | ||||||||||||||
added: v8.4.0 | ||||||||||||||
changes: | ||||||||||||||
- version: REPLACEME | ||||||||||||||
pr-url: https://github.com/nodejs/node/pull/34664 | ||||||||||||||
description: Requests with the `host` header (with or without | ||||||||||||||
`:authority`) can now be sent / received. | ||||||||||||||
- version: v10.10.0 | ||||||||||||||
pr-url: https://github.com/nodejs/node/pull/22466 | ||||||||||||||
description: HTTP/2 is now Stable. Previously, it had been Experimental. | ||||||||||||||
|
@@ -2530,7 +2534,7 @@ For incoming headers: | |||||||||||||
`access-control-max-age`, `access-control-request-method`, `content-encoding`, | ||||||||||||||
`content-language`, `content-length`, `content-location`, `content-md5`, | ||||||||||||||
`content-range`, `content-type`, `date`, `dnt`, `etag`, `expires`, `from`, | ||||||||||||||
`if-match`, `if-modified-since`, `if-none-match`, `if-range`, | ||||||||||||||
`host`, `if-match`, `if-modified-since`, `if-none-match`, `if-range`, | ||||||||||||||
`if-unmodified-since`, `last-modified`, `location`, `max-forwards`, | ||||||||||||||
`proxy-authorization`, `range`, `referer`,`retry-after`, `tk`, | ||||||||||||||
`upgrade-insecure-requests`, `user-agent` or `x-content-type-options` are | ||||||||||||||
|
@@ -2908,8 +2912,9 @@ added: v8.4.0 | |||||||||||||
|
||||||||||||||
* {string} | ||||||||||||||
|
||||||||||||||
The request authority pseudo header field. It can also be accessed via | ||||||||||||||
`req.headers[':authority']`. | ||||||||||||||
The request authority pseudo header field. Because HTTP/2 allows requests | ||||||||||||||
to set either `:authority` or `host`, this value is derived from | ||||||||||||||
`req.headers[':authority']` if present, `req.headers['host']` otherwise. | ||||||||||||||
mildsunrise marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
|
||||||||||||||
#### `request.complete` | ||||||||||||||
<!-- YAML | ||||||||||||||
|
@@ -3708,6 +3713,25 @@ following additional properties: | |||||||||||||
* `type` {string} Either `'server'` or `'client'` to identify the type of | ||||||||||||||
`Http2Session`. | ||||||||||||||
|
||||||||||||||
## Note on `:authority` and `host` | ||||||||||||||
|
||||||||||||||
HTTP/2 requires requests to have either the `:authority` pseudo-header | ||||||||||||||
or the `host` header. Using `:authority` should be preferred when | ||||||||||||||
constructing an HTTP/2 request directly, and `host` should be | ||||||||||||||
preferred when converting from HTTP/1 (in proxies, for instance). | ||||||||||||||
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. Optional suggestion:
Suggested change
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. Thanks for the pointers 💙 I'm not sure on this one, though... the spec only says it's a "preferred form", not imperative or anything, and I'm not sure how often this preference is honored in practice... (we didn't even allow that form to begin with) 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 think I'll apply the suggestion with |
||||||||||||||
|
||||||||||||||
Historically, Node.js required all requests to have the `:authority` | ||||||||||||||
pseudo-header and rejected requests with the `host` header present. | ||||||||||||||
This has been corrected; the `host` header is now allowed, and the | ||||||||||||||
only requirement is for either `host` or `:authority` to be | ||||||||||||||
present (or both). The same validations from `:authority` extend | ||||||||||||||
to `host`. | ||||||||||||||
mildsunrise marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
|
||||||||||||||
The compatibility API falls back to `host` if `:authority` is not | ||||||||||||||
present, see [`request.authority`][]. However, if you don't use the | ||||||||||||||
mildsunrise marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
compatibility API (or use `req.headers` directly), you need to | ||||||||||||||
implement any fall-back behaviour yourself. | ||||||||||||||
|
||||||||||||||
[ALPN Protocol ID]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids | ||||||||||||||
[ALPN negotiation]: #http2_alpn_negotiation | ||||||||||||||
[Compatibility API]: #http2_compatibility_api | ||||||||||||||
|
@@ -3748,6 +3772,7 @@ following additional properties: | |||||||||||||
[`net.Socket.prototype.unref()`]: net.html#net_socket_unref | ||||||||||||||
[`net.Socket`]: net.html#net_class_net_socket | ||||||||||||||
[`net.connect()`]: net.html#net_net_connect | ||||||||||||||
[`request.authority`]: #http2_request_authority | ||||||||||||||
[`request.socket`]: #http2_request_socket | ||||||||||||||
[`request.socket.getPeerCertificate()`]: tls.html#tls_tlssocket_getpeercertificate_detailed | ||||||||||||||
[`response.end()`]: #http2_response_end_data_encoding_callback | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
'use strict'; | ||
|
||
const common = require('../common'); | ||
if (!common.hasCrypto) | ||
common.skip('missing crypto'); | ||
const assert = require('assert'); | ||
const h2 = require('http2'); | ||
|
||
// Requests using host instead of :authority should be allowed | ||
// and Http2ServerRequest.authority should fall back to host | ||
|
||
// :authority should NOT be auto-filled if host is present | ||
|
||
const server = h2.createServer(); | ||
server.listen(0, common.mustCall(function() { | ||
const port = server.address().port; | ||
server.once('request', common.mustCall(function(request, response) { | ||
const expected = { | ||
':path': '/foobar', | ||
':method': 'GET', | ||
':scheme': 'http', | ||
'host': `localhost:${port}` | ||
}; | ||
|
||
assert.strictEqual(request.authority, expected.host); | ||
|
||
const headers = request.headers; | ||
for (const [name, value] of Object.entries(expected)) { | ||
assert.strictEqual(headers[name], value); | ||
} | ||
|
||
const rawHeaders = request.rawHeaders; | ||
for (const [name, value] of Object.entries(expected)) { | ||
const position = rawHeaders.indexOf(name); | ||
assert.notStrictEqual(position, -1); | ||
assert.strictEqual(rawHeaders[position + 1], value); | ||
} | ||
|
||
assert(!Object.hasOwnProperty.call(headers, ':authority')); | ||
assert(!Object.hasOwnProperty.call(rawHeaders, ':authority')); | ||
|
||
response.on('finish', common.mustCall(function() { | ||
server.close(); | ||
})); | ||
response.end(); | ||
})); | ||
|
||
const url = `http://localhost:${port}`; | ||
const client = h2.connect(url, common.mustCall(function() { | ||
const headers = { | ||
':path': '/foobar', | ||
':method': 'GET', | ||
':scheme': 'http', | ||
'host': `localhost:${port}` | ||
}; | ||
const request = client.request(headers); | ||
request.on('end', common.mustCall(function() { | ||
client.close(); | ||
})); | ||
request.end(); | ||
request.resume(); | ||
})); | ||
})); |
Uh oh!
There was an error while loading. Please reload this page.