diff --git a/lib/cookies/index.js b/lib/cookies/index.js index 8856332a58f..def04f5bc4a 100644 --- a/lib/cookies/index.js +++ b/lib/cookies/index.js @@ -1,10 +1,9 @@ 'use strict' const { parseSetCookie } = require('./parse') -const { stringify } = require('./util') +const { stringify, getHeadersList } = require('./util') const { webidl } = require('../fetch/webidl') const { Headers } = require('../fetch/headers') -const { kHeadersList } = require('../core/symbols') /** * @typedef {Object} Cookie @@ -27,9 +26,9 @@ const { kHeadersList } = require('../core/symbols') function getCookies (headers) { webidl.argumentLengthCheck(arguments, 1, { header: 'getCookies' }) - webidl.brandCheck(headers, Headers) + webidl.brandCheck(headers, Headers, { strict: false }) - const cookie = headers[kHeadersList].get('cookie') + const cookie = headers.get('cookie') const out = {} if (!cookie) { @@ -54,7 +53,7 @@ function getCookies (headers) { function deleteCookie (headers, name, attributes) { webidl.argumentLengthCheck(arguments, 2, { header: 'deleteCookie' }) - webidl.brandCheck(headers, Headers) + webidl.brandCheck(headers, Headers, { strict: false }) name = webidl.converters.DOMString(name) attributes = webidl.converters.DeleteCookieAttributes(attributes) @@ -76,9 +75,9 @@ function deleteCookie (headers, name, attributes) { function getSetCookies (headers) { webidl.argumentLengthCheck(arguments, 1, { header: 'getSetCookies' }) - webidl.brandCheck(headers, Headers) + webidl.brandCheck(headers, Headers, { strict: false }) - const cookies = headers[kHeadersList].cookies + const cookies = getHeadersList(headers).cookies if (!cookies) { return [] @@ -95,7 +94,7 @@ function getSetCookies (headers) { function setCookie (headers, cookie) { webidl.argumentLengthCheck(arguments, 2, { header: 'setCookie' }) - webidl.brandCheck(headers, Headers) + webidl.brandCheck(headers, Headers, { strict: false }) cookie = webidl.converters.Cookie(cookie) diff --git a/lib/cookies/util.js b/lib/cookies/util.js index d8c189fcd53..2290329fe8e 100644 --- a/lib/cookies/util.js +++ b/lib/cookies/util.js @@ -1,5 +1,8 @@ 'use strict' +const assert = require('assert') +const { kHeadersList } = require('../core/symbols') + function isCTLExcludingHtab (value) { if (value.length === 0) { return false @@ -260,7 +263,29 @@ function stringify (cookie) { return out.join('; ') } +let kHeadersListNode + +function getHeadersList (headers) { + if (headers[kHeadersList]) { + return headers[kHeadersList] + } + + if (!kHeadersListNode) { + kHeadersListNode = Object.getOwnPropertySymbols(headers).find( + (symbol) => symbol.description === 'headers list' + ) + + assert(kHeadersListNode, 'Headers cannot be parsed') + } + + const headersList = headers[kHeadersListNode] + assert(headersList) + + return headersList +} + module.exports = { isCTLExcludingHtab, - stringify + stringify, + getHeadersList } diff --git a/lib/fetch/webidl.js b/lib/fetch/webidl.js index e6eaaa499f6..e55de139505 100644 --- a/lib/fetch/webidl.js +++ b/lib/fetch/webidl.js @@ -33,9 +33,11 @@ webidl.errors.invalidArgument = function (context) { } // https://webidl.spec.whatwg.org/#implements -webidl.brandCheck = function (V, I) { - if (!(V instanceof I)) { +webidl.brandCheck = function (V, I, opts = undefined) { + if (opts?.strict !== false && !(V instanceof I)) { throw new TypeError('Illegal invocation') + } else { + return V?.[Symbol.toStringTag] === I.prototype[Symbol.toStringTag] } } diff --git a/test/cookie/global-headers.js b/test/cookie/global-headers.js new file mode 100644 index 00000000000..1d58dce1b60 --- /dev/null +++ b/test/cookie/global-headers.js @@ -0,0 +1,70 @@ +'use strict' + +const { test, skip } = require('tap') +const { + deleteCookie, + getCookies, + getSetCookies, + setCookie +} = require('../..') +const { getHeadersList } = require('../../lib/cookies/util') + +/* global Headers */ + +if (!globalThis.Headers) { + skip('No global Headers to test') + process.exit(0) +} + +test('Using global Headers', (t) => { + t.test('deleteCookies', (t) => { + const headers = new Headers() + + t.equal(headers.get('set-cookie'), null) + deleteCookie(headers, 'undici') + t.equal(headers.get('set-cookie'), 'undici=; Expires=Thu, 01 Jan 1970 00:00:00 GMT') + + t.end() + }) + + t.test('getCookies', (t) => { + const headers = new Headers({ + cookie: 'get=cookies; and=attributes' + }) + + t.same(getCookies(headers), { get: 'cookies', and: 'attributes' }) + t.end() + }) + + t.test('getSetCookies', (t) => { + const headers = new Headers({ + 'set-cookie': 'undici=getSetCookies; Secure' + }) + + const supportsCookies = getHeadersList(headers).cookies + + if (!supportsCookies) { + t.same(getSetCookies(headers), []) + } else { + t.same(getSetCookies(headers), [ + { + name: 'undici', + value: 'getSetCookies', + secure: true + } + ]) + } + + t.end() + }) + + t.test('setCookie', (t) => { + const headers = new Headers() + + setCookie(headers, { name: 'undici', value: 'setCookie' }) + t.equal(headers.get('Set-Cookie'), 'undici=setCookie') + t.end() + }) + + t.end() +}) diff --git a/types/cookies.d.ts b/types/cookies.d.ts index 5b2243440f0..aa38cae49d7 100644 --- a/types/cookies.d.ts +++ b/types/cookies.d.ts @@ -15,7 +15,7 @@ export interface Cookie { unparsed?: string[] } -export function deleteCookies ( +export function deleteCookie ( headers: Headers, name: string, attributes?: { name?: string, domain?: string } diff --git a/types/webidl.d.ts b/types/webidl.d.ts index ae33d2f87c1..182d18e0d4c 100644 --- a/types/webidl.d.ts +++ b/types/webidl.d.ts @@ -162,7 +162,7 @@ export interface Webidl { * @description Performs a brand-check on {@param V} to ensure it is a * {@param cls} object. */ - brandCheck (V: unknown, cls: Interface): asserts V is Interface + brandCheck (V: unknown, cls: Interface, opts?: { strict?: boolean }): asserts V is Interface /** * @see https://webidl.spec.whatwg.org/#es-sequence