Skip to content

Commit 45a760e

Browse files
committed
fix: don't lose cookies in middleware sequences
1 parent 9489885 commit 45a760e

11 files changed

Lines changed: 97 additions & 11 deletions

File tree

.changeset/open-cities-make.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fixes a bug that caused cookies to not be correctly set when using middleware sequences

packages/astro/src/core/middleware/sequence.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { MiddlewareHandler, RewritePayload } from '../../types/public/common.js';
22
import type { APIContext } from '../../types/public/context.js';
3-
import { AstroCookies } from '../cookies/cookies.js';
43
import { ForbiddenRewrite } from '../errors/errors-data.js';
54
import { AstroError } from '../errors/index.js';
65
import { getParams, type Pipeline } from '../render/index.js';
@@ -78,7 +77,6 @@ export function sequence(...handlers: MiddlewareHandler[]): MiddlewareHandler {
7877
carriedPayload = payload;
7978
handleContext.request = newRequest;
8079
handleContext.url = new URL(newRequest.url);
81-
handleContext.cookies = new AstroCookies(newRequest);
8280
handleContext.params = getParams(routeData, pathname);
8381
handleContext.routePattern = routeData.route;
8482
setOriginPathname(

packages/astro/src/core/render-context.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export class RenderContext {
131131
componentInstance: ComponentInstance | undefined,
132132
slots: Record<string, any> = {},
133133
): Promise<Response> {
134-
const { cookies, middleware, pipeline } = this;
134+
const { middleware, pipeline } = this;
135135
const { logger, serverLike, streaming, manifest } = pipeline;
136136

137137
const props =
@@ -271,7 +271,7 @@ export class RenderContext {
271271
// because they may need to be passed along from a rewrite.
272272
const responseCookies = getCookiesFromResponse(response);
273273
if (responseCookies) {
274-
cookies.merge(responseCookies);
274+
this.cookies.merge(responseCookies);
275275
}
276276
return response;
277277
};
@@ -289,7 +289,7 @@ export class RenderContext {
289289
// LEGACY: we put cookies on the response object,
290290
// where the adapter might be expecting to read it.
291291
// New code should be using `app.render({ addCookieHeader: true })` instead.
292-
attachCookiesToResponse(response, cookies);
292+
attachCookiesToResponse(response, this.cookies);
293293
return response;
294294
}
295295

@@ -346,7 +346,11 @@ export class RenderContext {
346346
);
347347
}
348348
this.url = new URL(this.request.url);
349-
this.cookies = new AstroCookies(this.request);
349+
const newCookies = new AstroCookies(this.request);
350+
if(this.cookies) {
351+
newCookies.merge(this.cookies);
352+
}
353+
this.cookies = newCookies;
350354
this.params = getParams(routeData, pathname);
351355
this.pathname = pathname;
352356
this.isRewriting = true;
@@ -363,15 +367,18 @@ export class RenderContext {
363367

364368
createActionAPIContext(): ActionAPIContext {
365369
const renderContext = this;
366-
const { cookies, params, pipeline, url } = this;
370+
const { params, pipeline, url } = this;
367371
const generator = `Astro v${ASTRO_VERSION}`;
368372

369373
const rewrite = async (reroutePayload: RewritePayload) => {
370374
return await this.#executeRewrite(reroutePayload);
371375
};
372376

373377
return {
374-
cookies,
378+
// Don't allow reassignment of cookies because it doesn't work
379+
get cookies() {
380+
return renderContext.cookies;
381+
},
375382
routePattern: this.routeData.route,
376383
isPrerendered: this.routeData.prerender,
377384
get clientAddress() {

packages/astro/test/fixtures/middleware-sequence-request-clone/src/middleware.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ import { sequence, defineMiddleware } from 'astro/middleware';
22

33
const middleware1 = defineMiddleware((_, next) => next('/'));
44

5-
const middleware2 = defineMiddleware(async ({ request }, next) => {
5+
const middleware2 = defineMiddleware(async ({ request, cookies }, next) => {
6+
cookies.set('cookie1', 'Cookie from middleware 1');
67
console.log(await request.clone().text());
78
return next();
89
});
910

10-
const middleware3 = defineMiddleware(async ({ request }, next) => {
11+
const middleware3 = defineMiddleware(async ({ request, cookies }, next) => {
12+
cookies.set('cookie2', 'Cookie from middleware 2');
1113
await request.clone();
1214
return next();
1315
});
1416

15-
export const onRequest = sequence(middleware1, middleware2, middleware3);
17+
export const onRequest = sequence(middleware1, middleware2, middleware3);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from 'astro/config';
2+
3+
// https://astro.build/config
4+
export default defineConfig({
5+
vite: {
6+
plugins: [],
7+
}
8+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "@test/middleware-sequence-rewrite",
3+
"version": "0.0.0",
4+
"private": true,
5+
"dependencies": {
6+
"astro": "workspace:*"
7+
}
8+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { MiddlewareHandler } from 'astro';
2+
import { sequence } from 'astro:middleware';
3+
4+
export const mid1: MiddlewareHandler = async ({ cookies, url, rewrite, request }, next) => {
5+
cookies.set('cookie1', 'Cookie from middleware 1');
6+
if (url.pathname === '/') {
7+
console.log('Rewriting');
8+
return rewrite('/another');
9+
}
10+
return next();
11+
};
12+
13+
export const mid2: MiddlewareHandler = async ({ cookies, url }, next) => {
14+
cookies.set('cookie2', 'Cookie from middleware 2');
15+
const res = next();
16+
return res;
17+
};
18+
19+
export const onRequest: MiddlewareHandler = sequence(mid1, mid2);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1>Hello Another</h1>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1>Hello Sequence</h1>

packages/astro/test/middleware.test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,4 +413,35 @@ describe('Middleware should support clone request', () => {
413413
const html = await res.text();
414414
assert.equal(html.includes('Hello Sequence and Request Clone'), true);
415415
});
416+
417+
it('should preserve cookies set in sequence', async () => {
418+
const res = await fixture.fetch('/');
419+
assert.ok(res.headers.get('set-cookie').includes('cookie1=Cookie%20from%20middleware%201'));
420+
assert.ok(res.headers.get('set-cookie').includes('cookie2=Cookie%20from%20middleware%202'));
421+
});
422+
});
423+
424+
describe('Middleware sequence rewrites', () => {
425+
/** @type {import('./test-utils').Fixture} */
426+
let fixture;
427+
let devServer;
428+
429+
before(async () => {
430+
fixture = await loadFixture({
431+
root: './fixtures/middleware-sequence-rewrite/',
432+
});
433+
devServer = await fixture.startDevServer();
434+
});
435+
436+
after(async () => {
437+
await devServer.stop();
438+
});
439+
440+
it('should preserve cookies set in sequence', async () => {
441+
const res = await fixture.fetch('/');
442+
const html = await res.text();
443+
assert.ok(html.includes('Hello Another'));
444+
assert.ok(res.headers.get('set-cookie').includes('cookie1=Cookie%20from%20middleware%201'));
445+
assert.ok(res.headers.get('set-cookie').includes('cookie2=Cookie%20from%20middleware%202'));
446+
});
416447
});

0 commit comments

Comments
 (0)