| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 3627616 commit 9c7e664
11 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2124,32 +2124,41 @@ Sends an HTTP/1.1 100 Continue message to the client, indicating that | |||
| 2124 | 2124 | the request body should be sent. See the [`'checkContinue'`][] event on | |
| 2125 | 2125 | `Server`. | |
| 2126 | 2126 | ||
| 2127 | - ### `response.writeEarlyHints(links[, callback])` | ||
| 2127 | + ### `response.writeEarlyHints(hints[, callback])` | ||
| 2128 | 2128 | ||
| 2129 | 2129 | <!-- YAML | |
| 2130 | 2130 | added: REPLACEME | |
| 2131 | + changes: | ||
| 2132 | + - version: REPLACEME | ||
| 2133 | + pr-url: https://github.com/nodejs/node/pull/44820 | ||
| 2134 | + description: Allow passing hints as an object. | ||
| 2131 | 2135 | --> | |
| 2132 | 2136 | ||
| 2133 | - * `links` {string|Array} | ||
| 2137 | + * `hints` {Object} | ||
| 2134 | 2138 | * `callback` {Function} | |
| 2135 | 2139 | ||
| 2136 | 2140 | Sends an HTTP/1.1 103 Early Hints message to the client with a Link header, | |
| 2137 | 2141 | indicating that the user agent can preload/preconnect the linked resources. | |
| 2138 | - The `links` can be a string or an array of strings containing the values | ||
| 2139 | - of the `Link` header. The optional `callback` argument will be called when | ||
| 2142 | + The `hints` is an object containing the values of headers to be sent with | ||
| 2143 | + early hints message. The optional `callback` argument will be called when | ||
| 2140 | 2144 | the response message has been written. | |
| 2141 | 2145 | ||
| 2142 | 2146 | **Example** | |
| 2143 | 2147 | ||
| 2144 | 2148 | ```js | |
| 2145 | 2149 | const earlyHintsLink = '</styles.css>; rel=preload; as=style'; | |
| 2146 | - response.writeEarlyHints(earlyHintsLink); | ||
| 2150 | + response.writeEarlyHints({ | ||
| 2151 | + 'link': earlyHintsLink, | ||
| 2152 | + }); | ||
| 2147 | 2153 | ||
| 2148 | 2154 | const earlyHintsLinks = [ | |
| 2149 | 2155 | '</styles.css>; rel=preload; as=style', | |
| 2150 | 2156 | '</scripts.js>; rel=preload; as=script', | |
| 2151 | 2157 | ]; | |
| 2152 | - response.writeEarlyHints(earlyHintsLinks); | ||
| 2158 | + response.writeEarlyHints({ | ||
| 2159 | + 'link': earlyHintsLinks, | ||
| 2160 | + 'x-trace-id': 'id for diagnostics' | ||
| 2161 | + }); | ||
| 2153 | 2162 | ||
| 2154 | 2163 | const earlyHintsCallback = () => console.log('early hints message sent'); | |
| 2155 | 2164 | response.writeEarlyHints(earlyHintsLinks, earlyHintsCallback); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -81,7 +81,8 @@ const { | |||
| 81 | 81 | const { | |
| 82 | 82 | validateInteger, | |
| 83 | 83 | validateBoolean, | |
| 84 | - validateLinkHeaderValue | ||
| 84 | + validateLinkHeaderValue, | ||
| 85 | + validateObject | ||
| 85 | 86 | } = require('internal/validators'); | |
| 86 | 87 | const Buffer = require('buffer').Buffer; | |
| 87 | 88 | const { | |
@@ -301,36 +302,27 @@ ServerResponse.prototype.writeProcessing = function writeProcessing(cb) { | |||
| 301 | 302 | this._writeRaw('HTTP/1.1 102 Processing\r\n\r\n', 'ascii', cb); | |
| 302 | 303 | }; | |
| 303 | 304 | ||
| 304 | - ServerResponse.prototype.writeEarlyHints = function writeEarlyHints(links, cb) { | ||
| 305 | + ServerResponse.prototype.writeEarlyHints = function writeEarlyHints(hints, cb) { | ||
| 305 | 306 | let head = 'HTTP/1.1 103 Early Hints\r\n'; | |
| 306 | 307 | ||
| 307 | - if (typeof links === 'string') { | ||
| 308 | - validateLinkHeaderValue(links, 'links'); | ||
| 309 | - head += 'Link: ' + links + '\r\n'; | ||
| 310 | - } else if (ArrayIsArray(links)) { | ||
| 311 | - if (!links.length) { | ||
| 312 | - return; | ||
| 313 | - } | ||
| 308 | + validateObject(hints, 'hints'); | ||
| 314 | 309 | ||
| 315 | - head += 'Link: '; | ||
| 310 | + if (hints.link === null || hints.link === undefined) { | ||
| 311 | + return; | ||
| 312 | + } | ||
| 316 | 313 | ||
| 317 | - for (let i = 0; i < links.length; i++) { | ||
| 318 | - const link = links[i]; | ||
| 319 | - validateLinkHeaderValue(link, 'links'); | ||
| 320 | - head += link; | ||
| 314 | + const link = validateLinkHeaderValue(hints.link); | ||
| 321 | 315 | ||
| 322 | - if (i !== links.length - 1) { | ||
| 323 | - head += ', '; | ||
| 324 | - } | ||
| 325 | - } | ||
| 316 | + if (link.length === 0) { | ||
| 317 | + return; | ||
| 318 | + } | ||
| 326 | 319 | ||
| 327 | - head += '\r\n'; | ||
| 328 | - } else { | ||
| 329 | - throw new ERR_INVALID_ARG_VALUE( | ||
| 330 | - 'links', | ||
| 331 | - links, | ||
| 332 | - 'must be an array or string of format "</styles.css>; rel=preload; as=style"' | ||
| 333 | - ); | ||
| 320 | + head += 'Link: ' + link + '\r\n'; | ||
| 321 | + | ||
| 322 | + for (const key of ObjectKeys(hints)) { | ||
| 323 | + if (key !== 'link') { | ||
| 324 | + head += key + ': ' + hints[key] + '\r\n'; | ||
| 325 | + } | ||
| 334 | 326 | } | |
| 335 | 327 | ||
| 336 | 328 | head += '\r\n'; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -57,6 +57,7 @@ const { | |||
| 57 | 57 | validateFunction, | |
| 58 | 58 | validateString, | |
| 59 | 59 | validateLinkHeaderValue, | |
| 60 | + validateObject, | ||
| 60 | 61 | } = require('internal/validators'); | |
| 61 | 62 | const { | |
| 62 | 63 | kSocket, | |
@@ -847,34 +848,21 @@ class Http2ServerResponse extends Stream { | |||
| 847 | 848 | return true; | |
| 848 | 849 | } | |
| 849 | 850 | ||
| 850 | - writeEarlyHints(links) { | ||
| 851 | - let linkHeaderValue = ''; | ||
| 851 | + writeEarlyHints(hints) { | ||
| 852 | + validateObject(hints, 'hints'); | ||
| 852 | 853 | ||
| 853 | - if (typeof links === 'string') { | ||
| 854 | - validateLinkHeaderValue(links, 'links'); | ||
| 855 | - linkHeaderValue += links; | ||
| 856 | - } else if (ArrayIsArray(links)) { | ||
| 857 | - if (!links.length) { | ||
| 858 | - return; | ||
| 859 | - } | ||
| 860 | - | ||
| 861 | - linkHeaderValue += ''; | ||
| 854 | + const headers = ObjectCreate(null); | ||
| 862 | 855 | ||
| 863 | - for (let i = 0; i < links.length; i++) { | ||
| 864 | - const link = links[i]; | ||
| 865 | - validateLinkHeaderValue(link, 'links'); | ||
| 866 | - linkHeaderValue += link; | ||
| 856 | + const linkHeaderValue = validateLinkHeaderValue(hints.link); | ||
| 867 | 857 | ||
| 868 | - if (i !== links.length - 1) { | ||
| 869 | - linkHeaderValue += ', '; | ||
| 870 | - } | ||
| 858 | + for (const key of ObjectKeys(hints)) { | ||
| 859 | + if (key !== 'link') { | ||
| 860 | + headers[key] = hints[key]; | ||
| 871 | 861 | } | |
| 872 | - } else { | ||
| 873 | - throw new ERR_INVALID_ARG_VALUE( | ||
| 874 | - 'links', | ||
| 875 | - links, | ||
| 876 | - 'must be an array or string of format "</styles.css>; rel=preload; as=style"' | ||
| 877 | - ); | ||
| 862 | + } | ||
| 863 | + | ||
| 864 | + if (linkHeaderValue.length === 0) { | ||
| 865 | + return false; | ||
| 878 | 866 | } | |
| 879 | 867 | ||
| 880 | 868 | const stream = this[kStream]; | |
@@ -883,8 +871,9 @@ class Http2ServerResponse extends Stream { | |||
| 883 | 871 | return false; | |
| 884 | 872 | ||
| 885 | 873 | stream.additionalHeaders({ | |
| 874 | + ...headers, | ||
| 886 | 875 | [HTTP2_HEADER_STATUS]: HTTP_STATUS_EARLY_HINTS, | |
| 887 | - 'Link': linkHeaderValue | ||
| 876 | + 'Link': linkHeaderValue, | ||
| 888 | 877 | }); | |
| 889 | 878 | ||
| 890 | 879 | return true; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -403,9 +403,13 @@ function validateUnion(value, name, union) { | |||
| 403 | 403 | } | |
| 404 | 404 | } | |
| 405 | 405 | ||
| 406 | - function validateLinkHeaderValue(value, name) { | ||
| 407 | - const linkValueRegExp = /^(?:<[^>]*>;)\s*(?:rel=(")?[^;"]*\1;?)\s*(?:(?:as|anchor|title)=(")?[^;"]*\2)?$/; | ||
| 406 | + const linkValueRegExp = /^(?:<[^>]*>;)\s*(?:rel=(")?[^;"]*\1;?)\s*(?:(?:as|anchor|title)=(")?[^;"]*\2)?$/; | ||
| 408 | 407 | ||
| 408 | + /** | ||
| 409 | + * @param {any} value | ||
| 410 | + * @param {string} name | ||
| 411 | + */ | ||
| 412 | + function validateLinkHeaderFormat(value, name) { | ||
| 409 | 413 | if ( | |
| 410 | 414 | typeof value === 'undefined' || | |
| 411 | 415 | !RegExpPrototypeExec(linkValueRegExp, value) | |
@@ -418,6 +422,42 @@ function validateLinkHeaderValue(value, name) { | |||
| 418 | 422 | } | |
| 419 | 423 | } | |
| 420 | 424 | ||
| 425 | + /** | ||
| 426 | + * @param {any} hints | ||
| 427 | + * @return {string} | ||
| 428 | + */ | ||
| 429 | + function validateLinkHeaderValue(hints) { | ||
| 430 | + if (typeof hints === 'string') { | ||
| 431 | + validateLinkHeaderFormat(hints, 'hints'); | ||
| 432 | + return hints; | ||
| 433 | + } else if (ArrayIsArray(hints)) { | ||
| 434 | + const hintsLength = hints.length; | ||
| 435 | + let result = ''; | ||
| 436 | + | ||
| 437 | + if (hintsLength === 0) { | ||
| 438 | + return result; | ||
| 439 | + } | ||
| 440 | + | ||
| 441 | + for (let i = 0; i < hintsLength; i++) { | ||
| 442 | + const link = hints[i]; | ||
| 443 | + validateLinkHeaderFormat(link, 'hints'); | ||
| 444 | + result += link; | ||
| 445 | + | ||
| 446 | + if (i !== hintsLength - 1) { | ||
| 447 | + result += ', '; | ||
| 448 | + } | ||
| 449 | + } | ||
| 450 | + | ||
| 451 | + return result; | ||
| 452 | + } | ||
| 453 | + | ||
| 454 | + throw new ERR_INVALID_ARG_VALUE( | ||
| 455 | + 'hints', | ||
| 456 | + hints, | ||
| 457 | + 'must be an array or string of format "</styles.css>; rel=preload; as=style"' | ||
| 458 | + ); | ||
| 459 | + } | ||
| 460 | + | ||
| 421 | 461 | module.exports = { | |
| 422 | 462 | isInt32, | |
| 423 | 463 | isUint32, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,7 +8,7 @@ const testResBody = 'response content\n'; | |||
| 8 | 8 | ||
| 9 | 9 | const server = http.createServer(common.mustCall((req, res) => { | |
| 10 | 10 | debug('Server sending early hints...'); | |
| 11 | - res.writeEarlyHints('bad argument value'); | ||
| 11 | + res.writeEarlyHints('bad argument type'); | ||
| 12 | 12 | ||
| 13 | 13 | debug('Server sending full response...'); | |
| 14 | 14 | res.end(testResBody); | |
@@ -27,7 +27,7 @@ server.listen(0, common.mustCall(() => { | |||
| 27 | 27 | process.on('uncaughtException', (err) => { | |
| 28 | 28 | debug(`Caught an exception: ${JSON.stringify(err)}`); | |
| 29 | 29 | if (err.name === 'AssertionError') throw err; | |
| 30 | - assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE'); | ||
| 30 | + assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE'); | ||
| 31 | 31 | process.exit(0); | |
| 32 | 32 | }); | |
| 33 | 33 | })); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments