Skip to content

Commit 8a30f25

Browse files
authored
fix(dev): break implicit rerouting loop (#10737)
* fix(dev): infinite implicit rerouting * test adapter * changeset
1 parent 2acea4d commit 8a30f25

File tree

4 files changed

+54
-50
lines changed

4 files changed

+54
-50
lines changed

.changeset/sharp-elephants-clap.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"astro": patch
3+
---
4+
5+
Fixes a regression where constructing and returning 404 responses from a middleware resulted in the dev server getting stuck in a loop.

packages/astro/src/vite-plugin-astro-server/route.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ export async function handleRoute({
287287
}
288288
if (response.status === 404 && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== 'no') {
289289
const fourOhFourRoute = await matchRoute('/404', manifestData, pipeline);
290-
if (options)
290+
if (options && options.route !== fourOhFourRoute?.route)
291291
return handleRoute({
292292
...options,
293293
matchedRoute: fourOhFourRoute,
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,67 @@
11
import assert from 'node:assert/strict';
22
import { after, before, describe, it } from 'node:test';
33
import { loadFixture } from './test-utils.js';
4+
import testAdapter from './test-adapter.js';
45

5-
for (const caseNumber of [1, 2, 3, 4]) {
6+
for (const caseNumber of [1, 2, 3, 4, 5]) {
67
describe(`Custom 404 with implicit rerouting - Case #${caseNumber}`, () => {
7-
/** @type Awaited<ReturnType<typeof loadFixture>> */
8+
/** @type {import('./test-utils.js').Fixture} */
89
let fixture;
9-
/** @type Awaited<ReturnType<typeof fixture['startDevServer']>> */
10-
let devServer;
1110

1211
before(async () => {
1312
fixture = await loadFixture({
13+
output: 'server',
1414
root: `./fixtures/custom-404-loop-case-${caseNumber}/`,
1515
site: 'http://example.com',
16+
adapter: testAdapter()
1617
});
17-
18-
devServer = await fixture.startDevServer();
1918
});
2019

21-
// sanity check
22-
it.skip(
23-
'dev server handles normal requests',
24-
{
25-
todo: 'To re-enabled after we understand why this fails when the test suite is run in parallel',
26-
},
27-
async () => {
28-
const resPromise = fixture.fetch('/');
29-
const result = await withTimeout(resPromise, 1000);
30-
assert.notEqual(result, timeout);
31-
assert.equal(result.status, 200);
32-
}
33-
);
20+
describe("dev server", () => {
21+
/** @type {import('./test-utils.js').DevServer} */
22+
let devServer;
3423

35-
it.skip(
36-
'dev server stays responsive',
37-
{
38-
todo: 'To re-enabled after we understand why this fails when the test suite is run in parallel',
39-
},
40-
async () => {
41-
const resPromise = fixture.fetch('/alvsibdlvjks');
42-
const result = await withTimeout(resPromise, 1000);
43-
assert.notEqual(result, timeout);
44-
assert.equal(result.status, 404);
45-
}
46-
);
24+
before(async () => {
25+
await fixture.build()
26+
devServer = await fixture.startDevServer();
27+
});
4728

48-
after(async () => {
49-
await devServer.stop();
29+
// sanity check
30+
it('dev server handles normal requests', { timeout: 1000 }, async () => {
31+
const response = await fixture.fetch('/');
32+
assert.equal(response.status, 200);
33+
});
34+
35+
// IMPORTANT: never skip
36+
it('dev server stays responsive', { timeout: 1000 }, async () => {
37+
const response = await fixture.fetch('/alvsibdlvjks');
38+
assert.equal(response.status, 404);
39+
});
40+
41+
after(async () => {
42+
await devServer.stop();
43+
});
5044
});
51-
});
52-
}
45+
46+
describe("prod server", () => {
47+
/** @type {import('./test-utils.js').App} */
48+
let app;
5349

54-
/***** UTILITY FUNCTIONS *****/
55-
56-
const timeout = Symbol('timeout');
57-
58-
/** @template Res */
59-
function withTimeout(
60-
/** @type Promise<Res> */
61-
responsePromise,
62-
/** @type number */
63-
timeLimit
64-
) {
65-
/** @type Promise<typeof timeout> */
66-
const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(timeout), timeLimit));
50+
before(async () => {
51+
app = await fixture.loadTestAdapterApp();
52+
});
6753

68-
return Promise.race([responsePromise, timeoutPromise]);
54+
// sanity check
55+
it('prod server handles normal requests', { timeout: 1000 }, async () => {
56+
const response = await app.render(new Request('https://example.com/'));
57+
assert.equal(response.status, 200);
58+
});
59+
60+
// IMPORTANT: never skip
61+
it('prod server stays responsive', { timeout: 1000 }, async () => {
62+
const response = await app.render(new Request('https://example.com/alvsibdlvjks'));
63+
assert.equal(response.status, 404);
64+
});
65+
});
66+
});
6967
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>all good here... or is it?</p>

0 commit comments

Comments
 (0)