Skip to content

Commit 4da45f9

Browse files
authored
use private properties in Headers (#3269)
1 parent 106bf1c commit 4da45f9

File tree

13 files changed

+119
-83
lines changed

13 files changed

+119
-83
lines changed

benchmarks/fetch/headers-length32.mjs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { bench, run } from 'mitata'
2-
import { Headers } from '../../lib/web/fetch/headers.js'
2+
import { Headers, getHeadersList } from '../../lib/web/fetch/headers.js'
33

44
const headers = new Headers(
55
[
@@ -38,11 +38,7 @@ const headers = new Headers(
3838
].map((v) => [v, ''])
3939
)
4040

41-
const kHeadersList = Reflect.ownKeys(headers).find(
42-
(c) => String(c) === 'Symbol(headers list)'
43-
)
44-
45-
const headersList = headers[kHeadersList]
41+
const headersList = getHeadersList(headers)
4642

4743
const kHeadersSortedMap = Reflect.ownKeys(headersList).find(
4844
(c) => String(c) === 'Symbol(headers map sorted)'

benchmarks/fetch/headers.mjs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { bench, group, run } from 'mitata'
2-
import { Headers } from '../../lib/web/fetch/headers.js'
2+
import { Headers, getHeadersList } from '../../lib/web/fetch/headers.js'
33

44
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
55
const charactersLength = characters.length
@@ -27,13 +27,9 @@ for (const [name, length] of Object.entries(settings)) {
2727

2828
const headersSorted = new Headers(headers)
2929

30-
const kHeadersList = Reflect.ownKeys(headers).find(
31-
(c) => String(c) === 'Symbol(headers list)'
32-
)
33-
34-
const headersList = headers[kHeadersList]
30+
const headersList = getHeadersList(headers)
3531

36-
const headersListSorted = headersSorted[kHeadersList]
32+
const headersListSorted = getHeadersList(headersSorted)
3733

3834
const kHeadersSortedMap = Reflect.ownKeys(headersList).find(
3935
(c) => String(c) === 'Symbol(headers map sorted)'

lib/core/symbols.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ module.exports = {
88
kQueue: Symbol('queue'),
99
kConnect: Symbol('connect'),
1010
kConnecting: Symbol('connecting'),
11-
kHeadersList: Symbol('headers list'),
1211
kKeepAliveDefaultTimeout: Symbol('default keep alive timeout'),
1312
kKeepAliveMaxTimeout: Symbol('max keep alive timeout'),
1413
kKeepAliveTimeoutThreshold: Symbol('keep alive timeout threshold'),

lib/web/cookies/util.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict'
22

33
const assert = require('node:assert')
4-
const { kHeadersList } = require('../../core/symbols')
4+
const { getHeadersList: internalGetHeadersList } = require('../fetch/headers')
55

66
/**
77
* @param {string} value
@@ -278,8 +278,10 @@ function stringify (cookie) {
278278
let kHeadersListNode
279279

280280
function getHeadersList (headers) {
281-
if (headers[kHeadersList]) {
282-
return headers[kHeadersList]
281+
try {
282+
return internalGetHeadersList(headers)
283+
} catch {
284+
// fall-through
283285
}
284286

285287
if (!kHeadersListNode) {

lib/web/fetch/headers.js

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
'use strict'
44

5-
const { kHeadersList, kConstruct } = require('../../core/symbols')
6-
const { kGuard } = require('./symbols')
5+
const { kConstruct } = require('../../core/symbols')
76
const { kEnumerableProperty } = require('../../core/util')
87
const {
98
iteratorMixin,
@@ -103,19 +102,18 @@ function appendHeader (headers, name, value) {
103102
// 3. If headers’s guard is "immutable", then throw a TypeError.
104103
// 4. Otherwise, if headers’s guard is "request" and name is a
105104
// forbidden header name, return.
105+
// 5. Otherwise, if headers’s guard is "request-no-cors":
106+
// TODO
106107
// Note: undici does not implement forbidden header names
107-
if (headers[kGuard] === 'immutable') {
108+
if (getHeadersGuard(headers) === 'immutable') {
108109
throw new TypeError('immutable')
109-
} else if (headers[kGuard] === 'request-no-cors') {
110-
// 5. Otherwise, if headers’s guard is "request-no-cors":
111-
// TODO
112110
}
113111

114112
// 6. Otherwise, if headers’s guard is "response" and name is a
115113
// forbidden response-header name, return.
116114

117115
// 7. Append (name, value) to headers’s header list.
118-
return headers[kHeadersList].append(name, value, false)
116+
return getHeadersList(headers).append(name, value, false)
119117

120118
// 8. If headers’s guard is "request-no-cors", then remove
121119
// privileged no-CORS request headers from headers
@@ -357,16 +355,20 @@ class HeadersList {
357355

358356
// https://fetch.spec.whatwg.org/#headers-class
359357
class Headers {
358+
#guard
359+
#headersList
360+
360361
constructor (init = undefined) {
361362
if (init === kConstruct) {
362363
return
363364
}
364-
this[kHeadersList] = new HeadersList()
365+
366+
this.#headersList = new HeadersList()
365367

366368
// The new Headers(init) constructor steps are:
367369

368370
// 1. Set this’s guard to "none".
369-
this[kGuard] = 'none'
371+
this.#guard = 'none'
370372

371373
// 2. If init is given, then fill this with init.
372374
if (init !== undefined) {
@@ -416,22 +418,20 @@ class Headers {
416418
// 5. Otherwise, if this’s guard is "response" and name is
417419
// a forbidden response-header name, return.
418420
// Note: undici does not implement forbidden header names
419-
if (this[kGuard] === 'immutable') {
421+
if (this.#guard === 'immutable') {
420422
throw new TypeError('immutable')
421-
} else if (this[kGuard] === 'request-no-cors') {
422-
// TODO
423423
}
424424

425425
// 6. If this’s header list does not contain name, then
426426
// return.
427-
if (!this[kHeadersList].contains(name, false)) {
427+
if (!this.#headersList.contains(name, false)) {
428428
return
429429
}
430430

431431
// 7. Delete name from this’s header list.
432432
// 8. If this’s guard is "request-no-cors", then remove
433433
// privileged no-CORS request headers from this.
434-
this[kHeadersList].delete(name, false)
434+
this.#headersList.delete(name, false)
435435
}
436436

437437
// https://fetch.spec.whatwg.org/#dom-headers-get
@@ -454,7 +454,7 @@ class Headers {
454454

455455
// 2. Return the result of getting name from this’s header
456456
// list.
457-
return this[kHeadersList].get(name, false)
457+
return this.#headersList.get(name, false)
458458
}
459459

460460
// https://fetch.spec.whatwg.org/#dom-headers-has
@@ -477,7 +477,7 @@ class Headers {
477477

478478
// 2. Return true if this’s header list contains name;
479479
// otherwise false.
480-
return this[kHeadersList].contains(name, false)
480+
return this.#headersList.contains(name, false)
481481
}
482482

483483
// https://fetch.spec.whatwg.org/#dom-headers-set
@@ -518,16 +518,14 @@ class Headers {
518518
// 6. Otherwise, if this’s guard is "response" and name is a
519519
// forbidden response-header name, return.
520520
// Note: undici does not implement forbidden header names
521-
if (this[kGuard] === 'immutable') {
521+
if (this.#guard === 'immutable') {
522522
throw new TypeError('immutable')
523-
} else if (this[kGuard] === 'request-no-cors') {
524-
// TODO
525523
}
526524

527525
// 7. Set (name, value) in this’s header list.
528526
// 8. If this’s guard is "request-no-cors", then remove
529527
// privileged no-CORS request headers from this
530-
this[kHeadersList].set(name, value, false)
528+
this.#headersList.set(name, value, false)
531529
}
532530

533531
// https://fetch.spec.whatwg.org/#dom-headers-getsetcookie
@@ -538,7 +536,7 @@ class Headers {
538536
// 2. Return the values of all headers in this’s header list whose name is
539537
// a byte-case-insensitive match for `Set-Cookie`, in order.
540538

541-
const list = this[kHeadersList].cookies
539+
const list = this.#headersList.cookies
542540

543541
if (list) {
544542
return [...list]
@@ -549,8 +547,8 @@ class Headers {
549547

550548
// https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
551549
get [kHeadersSortedMap] () {
552-
if (this[kHeadersList][kHeadersSortedMap]) {
553-
return this[kHeadersList][kHeadersSortedMap]
550+
if (this.#headersList[kHeadersSortedMap]) {
551+
return this.#headersList[kHeadersSortedMap]
554552
}
555553

556554
// 1. Let headers be an empty list of headers with the key being the name
@@ -559,14 +557,14 @@ class Headers {
559557

560558
// 2. Let names be the result of convert header names to a sorted-lowercase
561559
// set with all the names of the headers in list.
562-
const names = this[kHeadersList].toSortedArray()
560+
const names = this.#headersList.toSortedArray()
563561

564-
const cookies = this[kHeadersList].cookies
562+
const cookies = this.#headersList.cookies
565563

566564
// fast-path
567565
if (cookies === null || cookies.length === 1) {
568566
// Note: The non-null assertion of value has already been done by `HeadersList#toSortedArray`
569-
return (this[kHeadersList][kHeadersSortedMap] = names)
567+
return (this.#headersList[kHeadersSortedMap] = names)
570568
}
571569

572570
// 3. For each name of names:
@@ -596,16 +594,38 @@ class Headers {
596594
}
597595

598596
// 4. Return headers.
599-
return (this[kHeadersList][kHeadersSortedMap] = headers)
597+
return (this.#headersList[kHeadersSortedMap] = headers)
600598
}
601599

602600
[util.inspect.custom] (depth, options) {
603601
options.depth ??= depth
604602

605-
return `Headers ${util.formatWithOptions(options, this[kHeadersList].entries)}`
603+
return `Headers ${util.formatWithOptions(options, this.#headersList.entries)}`
604+
}
605+
606+
static getHeadersGuard (o) {
607+
return o.#guard
608+
}
609+
610+
static setHeadersGuard (o, guard) {
611+
o.#guard = guard
612+
}
613+
614+
static getHeadersList (o) {
615+
return o.#headersList
616+
}
617+
618+
static setHeadersList (o, list) {
619+
o.#headersList = list
606620
}
607621
}
608622

623+
const { getHeadersGuard, setHeadersGuard, getHeadersList, setHeadersList } = Headers
624+
Reflect.deleteProperty(Headers, 'getHeadersGuard')
625+
Reflect.deleteProperty(Headers, 'setHeadersGuard')
626+
Reflect.deleteProperty(Headers, 'getHeadersList')
627+
Reflect.deleteProperty(Headers, 'setHeadersList')
628+
609629
Object.defineProperty(Headers.prototype, util.inspect.custom, {
610630
enumerable: false
611631
})
@@ -631,8 +651,12 @@ webidl.converters.HeadersInit = function (V, prefix, argument) {
631651

632652
// A work-around to ensure we send the properly-cased Headers when V is a Headers object.
633653
// Read https://github.com/nodejs/undici/pull/3159#issuecomment-2075537226 before touching, please.
634-
if (!util.types.isProxy(V) && kHeadersList in V && iterator === Headers.prototype.entries) { // Headers object
635-
return V[kHeadersList].entriesList
654+
if (!util.types.isProxy(V) && iterator === Headers.prototype.entries) { // Headers object
655+
try {
656+
return getHeadersList(V).entriesList
657+
} catch {
658+
// fall-through
659+
}
636660
}
637661

638662
if (typeof iterator === 'function') {
@@ -654,5 +678,9 @@ module.exports = {
654678
// for test.
655679
compareHeaderName,
656680
Headers,
657-
HeadersList
681+
HeadersList,
682+
getHeadersGuard,
683+
setHeadersGuard,
684+
setHeadersList,
685+
getHeadersList
658686
}

lib/web/fetch/request.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
'use strict'
44

55
const { extractBody, mixinBody, cloneBody } = require('./body')
6-
const { Headers, fill: fillHeaders, HeadersList } = require('./headers')
6+
const { Headers, fill: fillHeaders, HeadersList, setHeadersGuard, getHeadersGuard, setHeadersList, getHeadersList } = require('./headers')
77
const { FinalizationRegistry } = require('./dispatcher-weakref')()
88
const util = require('../../core/util')
99
const nodeUtil = require('node:util')
@@ -25,10 +25,10 @@ const {
2525
requestDuplex
2626
} = require('./constants')
2727
const { kEnumerableProperty } = util
28-
const { kHeaders, kSignal, kState, kGuard, kDispatcher } = require('./symbols')
28+
const { kHeaders, kSignal, kState, kDispatcher } = require('./symbols')
2929
const { webidl } = require('./webidl')
3030
const { URLSerializer } = require('./data-url')
31-
const { kHeadersList, kConstruct } = require('../../core/symbols')
31+
const { kConstruct } = require('../../core/symbols')
3232
const assert = require('node:assert')
3333
const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = require('node:events')
3434

@@ -445,8 +445,8 @@ class Request {
445445
// Realm, whose header list is request’s header list and guard is
446446
// "request".
447447
this[kHeaders] = new Headers(kConstruct)
448-
this[kHeaders][kHeadersList] = request.headersList
449-
this[kHeaders][kGuard] = 'request'
448+
setHeadersList(this[kHeaders], request.headersList)
449+
setHeadersGuard(this[kHeaders], 'request')
450450

451451
// 31. If this’s request’s mode is "no-cors", then:
452452
if (mode === 'no-cors') {
@@ -459,13 +459,13 @@ class Request {
459459
}
460460

461461
// 2. Set this’s headers’s guard to "request-no-cors".
462-
this[kHeaders][kGuard] = 'request-no-cors'
462+
setHeadersGuard(this[kHeaders], 'request-no-cors')
463463
}
464464

465465
// 32. If init is not empty, then:
466466
if (initHasKey) {
467467
/** @type {HeadersList} */
468-
const headersList = this[kHeaders][kHeadersList]
468+
const headersList = getHeadersList(this[kHeaders])
469469
// 1. Let headers be a copy of this’s headers and its associated header
470470
// list.
471471
// 2. If init["headers"] exists, then set headers to init["headers"].
@@ -519,7 +519,7 @@ class Request {
519519
// 3, If Content-Type is non-null and this’s headers’s header list does
520520
// not contain `Content-Type`, then append `Content-Type`/Content-Type to
521521
// this’s headers.
522-
if (contentType && !this[kHeaders][kHeadersList].contains('content-type', true)) {
522+
if (contentType && !getHeadersList(this[kHeaders]).contains('content-type', true)) {
523523
this[kHeaders].append('content-type', contentType)
524524
}
525525
}
@@ -785,7 +785,7 @@ class Request {
785785
}
786786

787787
// 4. Return clonedRequestObject.
788-
return fromInnerRequest(clonedRequest, ac.signal, this[kHeaders][kGuard])
788+
return fromInnerRequest(clonedRequest, ac.signal, getHeadersGuard(this[kHeaders]))
789789
}
790790

791791
[nodeUtil.inspect.custom] (depth, options) {
@@ -894,8 +894,8 @@ function fromInnerRequest (innerRequest, signal, guard) {
894894
request[kState] = innerRequest
895895
request[kSignal] = signal
896896
request[kHeaders] = new Headers(kConstruct)
897-
request[kHeaders][kHeadersList] = innerRequest.headersList
898-
request[kHeaders][kGuard] = guard
897+
setHeadersList(request[kHeaders], innerRequest.headersList)
898+
setHeadersGuard(request[kHeaders], guard)
899899
return request
900900
}
901901

0 commit comments

Comments
 (0)