diff --git a/.changeset/good-badgers-sleep.md b/.changeset/good-badgers-sleep.md new file mode 100644 index 0000000..80fd5ab --- /dev/null +++ b/.changeset/good-badgers-sleep.md @@ -0,0 +1,5 @@ +--- +"@remix-run/web-fetch": patch +--- + +Fix `headers.entries`/`values`/`forEach` iteration for `Set-Cookie` headers diff --git a/packages/fetch/src/headers.js b/packages/fetch/src/headers.js index 26534e1..a5b566f 100644 --- a/packages/fetch/src/headers.js +++ b/packages/fetch/src/headers.js @@ -190,7 +190,14 @@ export default class Headers extends URLSearchParams { */ forEach(callback, thisArg = undefined) { for (const name of this.keys()) { - Reflect.apply(callback, thisArg, [this.get(name), name, this]); + if (name.toLowerCase() === 'set-cookie') { + let cookies = this.getAll(name); + while (cookies.length > 0) { + Reflect.apply(callback, thisArg, [cookies.shift(), name, this]) + } + } else { + Reflect.apply(callback, thisArg, [this.get(name), name, this]); + } } } @@ -199,7 +206,14 @@ export default class Headers extends URLSearchParams { */ * values() { for (const name of this.keys()) { - yield /** @type {string} */(this.get(name)); + if (name.toLowerCase() === 'set-cookie') { + let cookies = this.getAll(name); + while (cookies.length > 0) { + yield /** @type {string} */(cookies.shift()); + } + } else { + yield /** @type {string} */(this.get(name)); + } } } @@ -208,7 +222,14 @@ export default class Headers extends URLSearchParams { */ * entries() { for (const name of this.keys()) { - yield [name, /** @type {string} */(this.get(name))]; + if (name.toLowerCase() === 'set-cookie') { + let cookies = this.getAll(name); + while (cookies.length > 0) { + yield [name, /** @type {string} */(cookies.shift())]; + } + } else { + yield [name, /** @type {string} */(this.get(name))]; + } } } diff --git a/packages/fetch/test/headers.js b/packages/fetch/test/headers.js index 28bc70d..94354cc 100644 --- a/packages/fetch/test/headers.js +++ b/packages/fetch/test/headers.js @@ -69,6 +69,26 @@ describe('Headers', () => { expect({key: 'content-type', value: 'text/html', object: headers}).to.deep.equal(results[1]); }); + it('should allow iterating through multiple set-cookie headers with forEach', () => { + let headers = new Headers([ + ['a', '1'], + ['Set-Cookie', 'b=2'] + ]); + headers.append('Set-Cookie', 'c=3'); + expect(headers.entries()).to.be.iterable; + + const results = []; + headers.forEach((value, key, object) => { + results.push({value, key, object}); + }); + + expect(results).to.deep.equal([ + { value: '1', key: 'a', object: headers }, + { value: 'b=2', key: 'set-cookie', object: headers }, + { value: 'c=3', key: 'set-cookie', object: headers }, + ]); + }) + it('should set "this" to undefined by default on forEach', () => { const headers = new Headers({Accept: 'application/json'}); headers.forEach(function () { @@ -103,8 +123,25 @@ describe('Headers', () => { ['b', '2, 3'], ['c', '4'] ]); + }); + it('should allow iterating through multiple set-cookie headers with for-of loop', () => { + let headers = new Headers([ + ['a', '1'], + ['Set-Cookie', 'b=2'] + ]); + headers.append('Set-Cookie', 'c=3'); + expect(headers.entries()).to.be.iterable; + + const result = []; + for (const pair of headers) { + result.push(pair); + } + + expect(result).to.deep.equal([['a', '1'], ['set-cookie', 'b=2'], ['set-cookie', 'c=3']]); + }) + it('should allow iterating through all headers with entries()', () => { const headers = new Headers([ ['b', '2'], @@ -121,6 +158,16 @@ describe('Headers', () => { ]); }); + it('should allow iterating through multiple set-cookie headers with entries()', ()=> { + let headers = new Headers([ + ['a', '1'], + ['Set-Cookie', 'b=2'] + ]); + headers.append('Set-Cookie', 'c=3'); + expect(headers.entries()).to.be.iterable + .and.to.deep.iterate.over([['a', '1'], ['set-cookie', 'b=2'], ['set-cookie', 'c=3']]); + }) + it('should allow iterating through all headers with keys()', () => { const headers = new Headers([ ['b', '2'], @@ -145,6 +192,16 @@ describe('Headers', () => { .and.to.iterate.over(['1', '2, 3', '4']); }); + it('should allow iterating through multiple set-cookie headers with values()', ()=> { + let headers = new Headers([ + ['a', '1'], + ['Set-Cookie', 'b=2'] + ]); + headers.append('Set-Cookie', 'c=3'); + expect(headers.values()).to.be.iterable + .and.to.iterate.over(['1', 'b=2', 'c=3']); + }) + it('should reject illegal header', () => { const headers = new Headers(); expect(() => new Headers({'He y': 'ok'})).to.throw(TypeError);