Skip to content
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

fetch: replace HeadersList Array inheritance to Map #1309

Merged
merged 12 commits into from
Apr 9, 2022
93 changes: 34 additions & 59 deletions lib/fetch/headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,61 +91,33 @@ function fill (headers, object) {
}
}

// TODO: Composition over inheritence? Or helper methods?
class HeadersList extends Array {
class HeadersList extends Map {
RafaelGSS marked this conversation as resolved.
Show resolved Hide resolved
append (name, value) {
const normalizedName = normalizeAndValidateHeaderName(name)
const normalizedValue = normalizeAndValidateHeaderValue(name, value)

const index = binarySearch(this, normalizedName)
const exists = super.get(normalizedName)

if (this[index] === normalizedName) {
this[index + 1] += `, ${normalizedValue}`
if (exists) {
this.set(normalizedName, `${exists}, ${normalizedValue}`)
} else {
this.splice(index, 0, normalizedName, normalizedValue)
this.set(normalizedName, `${normalizedValue}`)
}
}

delete (name) {
const normalizedName = normalizeAndValidateHeaderName(name)

const index = binarySearch(this, normalizedName)

if (this[index] === normalizedName) {
this.splice(index, 2)
}
return super.delete(normalizedName)
}

get (name) {
const normalizedName = normalizeAndValidateHeaderName(name)

const index = binarySearch(this, normalizedName)

if (this[index] === normalizedName) {
return this[index + 1]
}

return null
return super.get(normalizedName) || null
}

has (name) {
const normalizedName = normalizeAndValidateHeaderName(name)

const index = binarySearch(this, normalizedName)

return this[index] === normalizedName
}

set (name, value) {
const normalizedName = normalizeAndValidateHeaderName(name)
const normalizedValue = normalizeAndValidateHeaderValue(name, value)

const index = binarySearch(this, normalizedName)
if (this[index] === normalizedName) {
this[index + 1] = normalizedValue
} else {
this.splice(index, 0, normalizedName, normalizedValue)
}
return super.has(normalizedName)
RafaelGSS marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -160,17 +132,26 @@ class Headers {
"Failed to construct 'Headers': The provided value is not of type '(record<ByteString, ByteString> or sequence<sequence<ByteString>>"
)
}
const init = args.length >= 1 ? args[0] ?? {} : {}

this[kHeadersList] = new HeadersList()

let init = {}
// The new Headers(init) constructor steps are:

// 1. Set this’s guard to "none".
this[kGuard] = 'none'

// 2. If init is given, then fill this with init.
fill(this, init)
// TODO(rafaelgss): it replaces fill
if (args.length >= 1) {
init = args[0] ?? {}
if (init[Symbol.iterator]) {
this[kHeadersList] = new HeadersList(init)
} else if (typeof init === 'object') {
this[kHeadersList] = new HeadersList(Object.entries(init))
} else {
throw new TypeError()
}
} else {
this[kHeadersList] = new HeadersList()
}
}

get [Symbol.toStringTag] () {
Expand Down Expand Up @@ -309,23 +290,25 @@ class Headers {
}

* keys () {
const clone = this[kHeadersList].slice()
for (let index = 0; index < clone.length; index += 2) {
yield clone[index]
console.log('called')
const listFreezed = new HeadersList(this[kHeadersList])
for (const key of listFreezed.keys()) {
yield key
}
}

* values () {
const clone = this[kHeadersList].slice()
for (let index = 1; index < clone.length; index += 2) {
yield clone[index]
const listFreezed = new HeadersList(this[kHeadersList])
for (const val of listFreezed.values()) {
yield val
}
}

* entries () {
const clone = this[kHeadersList].slice()
for (let index = 0; index < clone.length; index += 2) {
yield [clone[index], clone[index + 1]]
console.log('called')
const listFreezed = new HeadersList(this[kHeadersList])
RafaelGSS marked this conversation as resolved.
Show resolved Hide resolved
for (const [key, val] of listFreezed.entries()) {
yield [key, val]
}
}

Expand All @@ -346,15 +329,7 @@ class Headers {
const callback = args[0]
const thisArg = args[1]

const clone = this[kHeadersList].slice()
for (let index = 0; index < clone.length; index += 2) {
callback.call(
thisArg,
clone[index + 1],
clone[index],
this
)
}
this[kHeadersList].forEach(callback.bind(thisArg))
}

[Symbol.for('nodejs.util.inspect.custom')] () {
Expand Down
4 changes: 2 additions & 2 deletions lib/fetch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ async function schemeFetch (fetchParams) {
const resp = makeResponse({
statusText: 'OK',
headersList: [
'content-type', 'text/html;charset=utf-8'
['content-type', 'text/html;charset=utf-8']
]
})

Expand Down Expand Up @@ -871,7 +871,7 @@ async function schemeFetch (fetchParams) {
return makeResponse({
statusText: 'OK',
headersList: [
'content-type', contentType
['content-type', contentType]
],
body: extractBody(dataURLStruct.body)[0]
})
Expand Down
8 changes: 6 additions & 2 deletions lib/fetch/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,13 @@ class Request {
// 4. If headers is a Headers object, then for each header in its header
// list, append header’s name/header’s value to this’s headers.
if (headers instanceof Headers) {
this[kState].headersList.push(...headers[kHeadersList])
this[kState].headersList = new HeadersList([
...this[kState].headersList,
...headers[kHeadersList]
])
} else {
// 5. Otherwise, fill this’s headers with headers.
// TODO: check it before merge
fillHeaders(this[kState].headersList, headers)
}
}
Expand Down Expand Up @@ -793,7 +797,7 @@ function makeRequest (init) {
timingAllowFailed: false,
...init,
headersList: init.headersList
? new HeadersList(...init.headersList)
? new HeadersList(init.headersList)
: new HeadersList(),
urlList: init.urlList ? [...init.urlList.map((url) => new URL(url))] : []
}
Expand Down
2 changes: 1 addition & 1 deletion lib/fetch/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ function makeResponse (init) {
statusText: '',
...init,
headersList: init.headersList
? new HeadersList(...init.headersList)
? new HeadersList(init.headersList)
: new HeadersList(),
urlList: init.urlList ? [...init.urlList] : []
}
Expand Down
9 changes: 5 additions & 4 deletions test/fetch/headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ tap.test('Headers as Iterable', t => {
['bar', '456']
]
const expected = [
['x-bar', '456'],
['x-foo', '123']
['x-foo', '123'],
['x-bar', '456']
]
const headers = new Headers(init)
for (const [key, val] of headers) {
Expand All @@ -325,10 +325,11 @@ tap.test('Headers as Iterable', t => {
headers.set(key + key, 1)
}

t.strictSame(order, ['x', 'y', 'z'])
t.strictSame(order, ['z', 'y', 'x'])
// TODO: check if is required to keep the order
t.strictSame(
[...headers.keys()],
['x', 'xx', 'y', 'yy', 'z', 'zz']
['z', 'y', 'x', 'zz', 'yy', 'xx']
)
})

Expand Down