From 8a4e309f1178fcf299a533924226b143989396f6 Mon Sep 17 00:00:00 2001 From: David Ortner Date: Mon, 18 Mar 2024 12:07:46 +0100 Subject: [PATCH] chore: [#1315] Adds support for multiple values to Headers and removes logic for splitting cookie string --- .../cookie/urilities/CookieStringUtility.ts | 64 ------------------- packages/happy-dom/src/fetch/Headers.ts | 27 ++++---- .../urilities/CookieStringUtility.test.ts | 48 -------------- packages/happy-dom/test/fetch/Headers.test.ts | 25 ++++++++ 4 files changed, 37 insertions(+), 127 deletions(-) delete mode 100644 packages/happy-dom/test/cookie/urilities/CookieStringUtility.test.ts diff --git a/packages/happy-dom/src/cookie/urilities/CookieStringUtility.ts b/packages/happy-dom/src/cookie/urilities/CookieStringUtility.ts index c075fae81..f3370904f 100644 --- a/packages/happy-dom/src/cookie/urilities/CookieStringUtility.ts +++ b/packages/happy-dom/src/cookie/urilities/CookieStringUtility.ts @@ -113,68 +113,4 @@ export default class CookieStringUtility { return cookieString.join('; '); } - - /** - * Returns a list of Cookie String parsed from Cookies String. - * - * Based on: - * https://github.com/nfriedly/set-cookie-parser/blob/master/lib/set-cookie.js (MIT) - * - * @param cookiesString Cookies string. - * @returns Cookies strings. - */ - public static splitCookiesString(cookiesString: string): string[] { - let pos = 0; - - const skipWhiteSpace = (): boolean => { - while (pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))) { - pos += 1; - } - return pos < cookiesString.length; - }; - - const notSpecialChar = (): boolean => { - const ch = cookiesString.charAt(pos); - return ch !== '=' && ch !== ';' && ch !== ','; - }; - - const cookiesStrings: string[] = []; - - while (pos < cookiesString.length) { - let start = pos; - let cookiesSeparatorFound = false; - - while (skipWhiteSpace()) { - const ch = cookiesString.charAt(pos); - if (ch === ',') { - const lastComma = pos; - pos += 1; - - skipWhiteSpace(); - const nextStart = pos; - - while (pos < cookiesString.length && notSpecialChar()) { - pos += 1; - } - - if (pos < cookiesString.length && cookiesString.charAt(pos) === '=') { - cookiesSeparatorFound = true; - pos = nextStart; - cookiesStrings.push(cookiesString.substring(start, lastComma)); - start = pos; - } else { - pos = lastComma + 1; - } - } else { - pos += 1; - } - } - - if (!cookiesSeparatorFound || pos >= cookiesString.length) { - cookiesStrings.push(cookiesString.substring(start, cookiesString.length)); - } - } - - return cookiesStrings; - } } diff --git a/packages/happy-dom/src/fetch/Headers.ts b/packages/happy-dom/src/fetch/Headers.ts index 41ecc7d63..041c66767 100644 --- a/packages/happy-dom/src/fetch/Headers.ts +++ b/packages/happy-dom/src/fetch/Headers.ts @@ -3,7 +3,6 @@ import * as PropertySymbol from '../PropertySymbol.js'; import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum.js'; import IHeaders from './types/IHeaders.js'; import IHeadersInit from './types/IHeadersInit.js'; -import CookieStringUtility from '../cookie/urilities/CookieStringUtility.js'; /** * Fetch headers. @@ -11,7 +10,7 @@ import CookieStringUtility from '../cookie/urilities/CookieStringUtility.js'; * @see https://developer.mozilla.org/en-US/docs/Web/API/Headers */ export default class Headers implements IHeaders { - public [PropertySymbol.entries]: { [k: string]: { name: string; value: string } } = {}; + public [PropertySymbol.entries]: { [k: string]: { name: string; value: string[] } } = {}; /** * Constructor. @@ -49,11 +48,11 @@ export default class Headers implements IHeaders { public append(name: string, value: string): void { const lowerName = name.toLowerCase(); if (this[PropertySymbol.entries][lowerName]) { - this[PropertySymbol.entries][lowerName].value += `, ${value}`; + this[PropertySymbol.entries][lowerName].value.push(value); } else { this[PropertySymbol.entries][lowerName] = { name, - value + value: [value] }; } } @@ -74,7 +73,7 @@ export default class Headers implements IHeaders { * @returns Value. */ public get(name: string): string | null { - return this[PropertySymbol.entries][name.toLowerCase()]?.value ?? null; + return this[PropertySymbol.entries][name.toLowerCase()]?.value.join(', ') ?? null; } /** @@ -86,7 +85,7 @@ export default class Headers implements IHeaders { public set(name: string, value: string): void { this[PropertySymbol.entries][name.toLowerCase()] = { name, - value + value: [value] }; } @@ -96,13 +95,11 @@ export default class Headers implements IHeaders { * @returns An array of strings representing the values of all the different Set-Cookie headers. */ public getSetCookie(): string[] { - const cookiesString = this.get('Set-Cookie'); - if (cookiesString === null) { + const entry = this[PropertySymbol.entries]['set-cookie']; + if (!entry) { return []; - } else if (cookiesString === '') { - return ['']; } - return CookieStringUtility.splitCookiesString(cookiesString); + return entry.value; } /** @@ -122,7 +119,7 @@ export default class Headers implements IHeaders { */ public forEach(callback: (name: string, value: string, thisArg: IHeaders) => void): void { for (const header of Object.values(this[PropertySymbol.entries])) { - callback(header.value, header.name, this); + callback(header.value.join(', '), header.name, this); } } @@ -144,7 +141,7 @@ export default class Headers implements IHeaders { */ public *values(): IterableIterator { for (const header of Object.values(this[PropertySymbol.entries])) { - yield header.value; + yield header.value.join(', '); } } @@ -155,7 +152,7 @@ export default class Headers implements IHeaders { */ public *entries(): IterableIterator<[string, string]> { for (const header of Object.values(this[PropertySymbol.entries])) { - yield [header.name, header.value]; + yield [header.name, header.value.join(', ')]; } } @@ -166,7 +163,7 @@ export default class Headers implements IHeaders { */ public *[Symbol.iterator](): IterableIterator<[string, string]> { for (const header of Object.values(this[PropertySymbol.entries])) { - yield [header.name, header.value]; + yield [header.name, header.value.join(', ')]; } } } diff --git a/packages/happy-dom/test/cookie/urilities/CookieStringUtility.test.ts b/packages/happy-dom/test/cookie/urilities/CookieStringUtility.test.ts deleted file mode 100644 index dddc17189..000000000 --- a/packages/happy-dom/test/cookie/urilities/CookieStringUtility.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import CookieStringUtility from '../../../src/cookie/urilities/CookieStringUtility.js'; -import { describe, it, expect } from 'vitest'; - -describe('CookieStringUtility', () => { - describe('splitCookiesString()', () => { - it('Should parse empty string.', () => { - expect(CookieStringUtility.splitCookiesString('')).toEqual([]); - }); - - it('Should parse single cookie without params.', () => { - expect(CookieStringUtility.splitCookiesString('key=value')).toEqual(['key=value']); - }); - - it('Should parse single cookie with params.', () => { - expect( - CookieStringUtility.splitCookiesString( - 'key=value; HttpOnly; Path=/; Expires=Fri, 01 Jan 2100 00:00:00 GMT' - ) - ).toEqual(['key=value; HttpOnly; Path=/; Expires=Fri, 01 Jan 2100 00:00:00 GMT']); - }); - - it('Should parse multiple cookies without params.', () => { - expect(CookieStringUtility.splitCookiesString('key1=value1, key2=value2')).toEqual([ - 'key1=value1', - 'key2=value2' - ]); - }); - - it('Should parse multiple cookies with params.', () => { - expect( - CookieStringUtility.splitCookiesString( - 'key1=value1; HttpOnly; Path=/; Expires=Fri, 01 Jan 2100 00:00:00 GMT, key2=value2; Domain=example.com; Max-Age=1' - ) - ).toEqual([ - 'key1=value1; HttpOnly; Path=/; Expires=Fri, 01 Jan 2100 00:00:00 GMT', - 'key2=value2; Domain=example.com; Max-Age=1' - ]); - }); - - it('Should parse multiple cookies including those with and without params.', () => { - expect( - CookieStringUtility.splitCookiesString( - 'key1=value1, key2=value2; Expires=Fri, 01 Jan 2100 00:00:00 GMT; Path=/' - ) - ).toEqual(['key1=value1', 'key2=value2; Expires=Fri, 01 Jan 2100 00:00:00 GMT; Path=/']); - }); - }); -}); diff --git a/packages/happy-dom/test/fetch/Headers.test.ts b/packages/happy-dom/test/fetch/Headers.test.ts index 6fb7cdc74..d20abf289 100644 --- a/packages/happy-dom/test/fetch/Headers.test.ts +++ b/packages/happy-dom/test/fetch/Headers.test.ts @@ -132,6 +132,31 @@ describe('Headers', () => { 'a=1', 'b=2; Expires=Fri, 01 Jan 2100 00:00:00 GMT' ]); + + const headers2 = new Headers(); + + headers2.append( + 'Set-Cookie', + 'key=value; HttpOnly; Path=/; Expires=Fri, 01 Jan 2100 00:00:00 GMT' + ); + + expect(headers2.getSetCookie()).toEqual([ + 'key=value; HttpOnly; Path=/; Expires=Fri, 01 Jan 2100 00:00:00 GMT' + ]); + + const headers3 = new Headers(); + + headers3.append( + 'Set-Cookie', + 'key1=value1; HttpOnly; Path=/; Expires=Fri, 01 Jan 2100 00:00:00 GMT' + ); + + headers3.append('Set-Cookie', 'key2=value2; Domain=example.com; Max-Age=1'); + + expect(headers3.getSetCookie()).toEqual([ + 'key1=value1; HttpOnly; Path=/; Expires=Fri, 01 Jan 2100 00:00:00 GMT', + 'key2=value2; Domain=example.com; Max-Age=1' + ]); }); });