Skip to content

Commit 77a5b0c

Browse files
fix: Ensure to delete cookies when switching from single to chunks and vica versa (#2013)
2 parents 0d7bfc4 + a717b5c commit 77a5b0c

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

src/server/chunked-cookies.test.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,49 @@ describe("Chunked Cookie Utils", () => {
166166
);
167167
});
168168

169+
it("should clear existing chunked cookies when setting a single cookie", () => {
170+
const name = "testCookie";
171+
const value = "small value";
172+
const options = { path: "/" } as CookieOptions;
173+
174+
const chunk0 = "chunk0 value";
175+
const chunk1 = "chunk1 value";
176+
const chunk2 = "chunk2 value";
177+
178+
cookieStore.set(`${name}__1`, chunk1);
179+
cookieStore.set(`${name}__0`, chunk0);
180+
cookieStore.set(`${name}__2`, chunk2);
181+
182+
setChunkedCookie(name, value, options, reqCookies, resCookies);
183+
184+
expect(resCookies.set).toHaveBeenCalledTimes(1);
185+
expect(resCookies.set).toHaveBeenCalledWith(name, value, options);
186+
expect(reqCookies.set).toHaveBeenCalledTimes(1);
187+
expect(reqCookies.set).toHaveBeenCalledWith(name, value);
188+
expect(reqCookies.delete).toHaveBeenCalledTimes(3);
189+
expect(reqCookies.delete).toHaveBeenCalledWith(`${name}__0`);
190+
expect(reqCookies.delete).toHaveBeenCalledWith(`${name}__1`);
191+
expect(reqCookies.delete).toHaveBeenCalledWith(`${name}__2`);
192+
});
193+
194+
it("should clear existing single cookies when setting a chunked cookie", () => {
195+
const name = "testCookie";
196+
const value = "small value";
197+
198+
cookieStore.set(`${name}`, value);
199+
200+
// Create a large string (8000 bytes)
201+
const largeValue = "a".repeat(8000);
202+
const options = { path: "/" } as CookieOptions;
203+
204+
setChunkedCookie(name, largeValue, options, reqCookies, resCookies);
205+
206+
expect(reqCookies.delete).toHaveBeenCalledTimes(1);
207+
expect(reqCookies.delete).toHaveBeenCalledWith(`${name}`);
208+
expect(resCookies.set).toHaveBeenCalledTimes(3);
209+
expect(reqCookies.set).toHaveBeenCalledTimes(3);
210+
});
211+
169212
it("should clean up unused chunks when cookie shrinks", () => {
170213
const name = "testCookie";
171214
const options = { path: "/" } as CookieOptions;
@@ -185,9 +228,13 @@ describe("Chunked Cookie Utils", () => {
185228
const largeValue = "a".repeat(8000);
186229
setChunkedCookie(name, largeValue, options, reqCookies, resCookies);
187230

188-
expect(reqCookies.delete).toHaveBeenCalledTimes(2);
231+
// It is called 3 times.
232+
// 2 times for the chunks
233+
// 1 time for the non chunked cookie
234+
expect(reqCookies.delete).toHaveBeenCalledTimes(3);
189235
expect(reqCookies.delete).toHaveBeenCalledWith(`${name}__3`);
190236
expect(reqCookies.delete).toHaveBeenCalledWith(`${name}__4`);
237+
expect(reqCookies.delete).toHaveBeenCalledWith(name);
191238
});
192239

193240
describe("getChunkedCookie", () => {

src/server/cookies.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@ export function setChunkedCookie(
185185
resCookies.set(name, value, options);
186186
// to enable read-after-write in the same request for middleware
187187
reqCookies.set(name, value);
188+
189+
// When we are writing a non-chunked cookie, we should remove the chunked cookies
190+
getAllChunkedCookies(reqCookies, name).forEach(cookieChunk => {
191+
resCookies.delete(cookieChunk.name);
192+
reqCookies.delete(cookieChunk.name);
193+
});
194+
188195
return;
189196
}
190197

@@ -215,6 +222,10 @@ export function setChunkedCookie(
215222
reqCookies.delete(chunkName);
216223
}
217224
}
225+
226+
// When we have written chunked cookies, we should remove the non-chunked cookie
227+
resCookies.delete(name);
228+
reqCookies.delete(name);
218229
}
219230

220231
/**

0 commit comments

Comments
 (0)