Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions lib/web/fetch/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ class Request {
}

// 4. Set request to a new request whose URL is parsedURL.
request = makeRequest({ urlList: [parsedURL] })
// Note: Step 12 copies this request into a new one and makeRequest fills
// every missing field with its default, so the URL list is all it needs.
request = { urlList: [parsedURL] }

// 5. Set fallbackMode to "cors".
fallbackMode = 'cors'
Expand Down Expand Up @@ -486,29 +488,23 @@ class Request {
}

// 32. If init is not empty, then:
if (initHasKey) {
/** @type {HeadersList} */
const headersList = getHeadersList(this.#headers)
// Note: Without init["headers"], these steps empty the header list and
// append a copy of it back, which leaves it unchanged, so they are skipped.
// init["headers"] is never a Headers object here: the HeadersInit
// converter turns one into a list of its entries, which fill() appends.
if (initHasKey && init.headers !== undefined) {
// 1. Let headers be a copy of this’s headers and its associated header
// list.
// 2. If init["headers"] exists, then set headers to init["headers"].
const headers = init.headers !== undefined ? init.headers : new HeadersList(headersList)
const headers = init.headers

// 3. Empty this’s headers’s header list.
headersList.clear()
getHeadersList(this.#headers).clear()

// 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 HeadersList) {
for (const { name, value } of headers.rawValues()) {
headersList.append(name, value, false)
}
// Note: Copy the `set-cookie` meta-data.
headersList.cookies = headers.cookies
} else {
// 5. Otherwise, fill this’s headers with headers.
fillHeaders(this.#headers, headers)
}
// 5. Otherwise, fill this’s headers with headers.
fillHeaders(this.#headers, headers)
}

// 33. Let inputBody be input’s request’s body if input is a Request
Expand Down
25 changes: 25 additions & 0 deletions test/fetch/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,31 @@ test('Clone the set-cookie header when Request is passed as the first parameter
t.assert.strictEqual(request2.headers.getSetCookie().join(', '), request2.headers.get('set-cookie'))
})

test('Request with a Request input and an init without headers keeps a copy of the headers', (t) => {
const request = new Request('http://localhost', {
headers: [['Set-Cookie', 'a=1'], ['X-B', '2'], ['set-cookie', 'c=3'], ['x-a', '1']]
})
const request2 = new Request(request, { method: 'PUT' })

t.assert.strictEqual(request2.method, 'PUT')
t.assert.deepStrictEqual([...request2.headers], [...request.headers])
t.assert.deepStrictEqual(request2.headers.getSetCookie(), ['a=1', 'c=3'])

request2.headers.append('set-cookie', 'd=4')
request.headers.append('x-c', '3')
t.assert.deepStrictEqual(request.headers.getSetCookie(), ['a=1', 'c=3'])
t.assert.deepStrictEqual(request2.headers.getSetCookie(), ['a=1', 'c=3', 'd=4'])
t.assert.strictEqual(request2.headers.get('x-c'), null)
})

test('Request with a Headers object in init keeps all of its entries', (t) => {
const headers = new Headers([['X-A', '1'], ['set-cookie', 'a=1'], ['set-cookie', 'b=2']])
const request = new Request('http://localhost', { headers: { 'x-old': '0' } })
const request2 = new Request(request, { headers })

t.assert.deepStrictEqual([...request2.headers], [['set-cookie', 'a=1'], ['set-cookie', 'b=2'], ['x-a', '1']])
})

// Tests for optimization introduced in https://github.com/nodejs/undici/pull/2456
test('keys to object prototypes method', (t) => {
const request = new Request('http://localhost', { method: 'hasOwnProperty' })
Expand Down