0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00

fix(dev): break implicit rerouting loop (#10737)

* fix(dev): infinite implicit rerouting

* test adapter

* changeset
This commit is contained in:
Arsh 2024-04-10 06:25:33 +05:30 committed by GitHub
parent 2acea4defd
commit 8a30f257b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 55 additions and 51 deletions

View file

@ -0,0 +1,5 @@
---
"astro": patch
---
Fixes a regression where constructing and returning 404 responses from a middleware resulted in the dev server getting stuck in a loop.

View file

@ -287,7 +287,7 @@ export async function handleRoute({
}
if (response.status === 404 && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== 'no') {
const fourOhFourRoute = await matchRoute('/404', manifestData, pipeline);
if (options)
if (options && options.route !== fourOhFourRoute?.route)
return handleRoute({
...options,
matchedRoute: fourOhFourRoute,

View file

@ -1,69 +1,67 @@
import assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { loadFixture } from './test-utils.js';
import testAdapter from './test-adapter.js';
for (const caseNumber of [1, 2, 3, 4]) {
for (const caseNumber of [1, 2, 3, 4, 5]) {
describe(`Custom 404 with implicit rerouting - Case #${caseNumber}`, () => {
/** @type Awaited<ReturnType<typeof loadFixture>> */
/** @type {import('./test-utils.js').Fixture} */
let fixture;
/** @type Awaited<ReturnType<typeof fixture['startDevServer']>> */
let devServer;
before(async () => {
fixture = await loadFixture({
output: 'server',
root: `./fixtures/custom-404-loop-case-${caseNumber}/`,
site: 'http://example.com',
adapter: testAdapter()
});
});
describe("dev server", () => {
/** @type {import('./test-utils.js').DevServer} */
let devServer;
before(async () => {
await fixture.build()
devServer = await fixture.startDevServer();
});
// sanity check
it.skip(
'dev server handles normal requests',
{
todo: 'To re-enabled after we understand why this fails when the test suite is run in parallel',
},
async () => {
const resPromise = fixture.fetch('/');
const result = await withTimeout(resPromise, 1000);
assert.notEqual(result, timeout);
assert.equal(result.status, 200);
}
);
it('dev server handles normal requests', { timeout: 1000 }, async () => {
const response = await fixture.fetch('/');
assert.equal(response.status, 200);
});
it.skip(
'dev server stays responsive',
{
todo: 'To re-enabled after we understand why this fails when the test suite is run in parallel',
},
async () => {
const resPromise = fixture.fetch('/alvsibdlvjks');
const result = await withTimeout(resPromise, 1000);
assert.notEqual(result, timeout);
assert.equal(result.status, 404);
}
);
// IMPORTANT: never skip
it('dev server stays responsive', { timeout: 1000 }, async () => {
const response = await fixture.fetch('/alvsibdlvjks');
assert.equal(response.status, 404);
});
after(async () => {
await devServer.stop();
});
});
}
/***** UTILITY FUNCTIONS *****/
const timeout = Symbol('timeout');
/** @template Res */
function withTimeout(
/** @type Promise<Res> */
responsePromise,
/** @type number */
timeLimit
) {
/** @type Promise<typeof timeout> */
const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(timeout), timeLimit));
return Promise.race([responsePromise, timeoutPromise]);
describe("prod server", () => {
/** @type {import('./test-utils.js').App} */
let app;
before(async () => {
app = await fixture.loadTestAdapterApp();
});
// sanity check
it('prod server handles normal requests', { timeout: 1000 }, async () => {
const response = await app.render(new Request('https://example.com/'));
assert.equal(response.status, 200);
});
// IMPORTANT: never skip
it('prod server stays responsive', { timeout: 1000 }, async () => {
const response = await app.render(new Request('https://example.com/alvsibdlvjks'));
assert.equal(response.status, 404);
});
});
});
}

View file

@ -0,0 +1 @@
<p>all good here... or is it?</p>