diff --git a/.changeset/twenty-cherries-switch.md b/.changeset/twenty-cherries-switch.md new file mode 100644 index 0000000000..c79c53284e --- /dev/null +++ b/.changeset/twenty-cherries-switch.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes a bug that caused the dev server to return an error if requesting "//" diff --git a/packages/astro/src/core/routing/3xx.ts b/packages/astro/src/core/routing/3xx.ts index fd4e211ed7..c05d7a894f 100644 --- a/packages/astro/src/core/routing/3xx.ts +++ b/packages/astro/src/core/routing/3xx.ts @@ -1,5 +1,5 @@ export type RedirectTemplate = { - from: string; + from?: string; location: string | URL; status: number; }; @@ -14,6 +14,6 @@ export function redirectTemplate({ status, location, from }: RedirectTemplate) {
- Redirecting from${from}
to ${location}
+ Redirecting ${from ? `from ${from}
` : ''}to ${location}
`;
}
diff --git a/packages/astro/src/vite-plugin-astro-server/base.ts b/packages/astro/src/vite-plugin-astro-server/base.ts
index 84608ba508..4aa7e2a2d9 100644
--- a/packages/astro/src/vite-plugin-astro-server/base.ts
+++ b/packages/astro/src/vite-plugin-astro-server/base.ts
@@ -7,7 +7,9 @@ import { appendForwardSlash } from '@astrojs/internal-helpers/path';
import { bold } from 'kleur/colors';
import type { Logger } from '../core/logger/core.js';
import notFoundTemplate, { subpathNotUsedTemplate } from '../template/4xx.js';
-import { writeHtmlResponse } from './response.js';
+import { writeHtmlResponse, writeRedirectResponse } from './response.js';
+
+const manySlashes = /\/{2,}$/;
export function baseMiddleware(
settings: AstroSettings,
@@ -21,7 +23,10 @@ export function baseMiddleware(
return function devBaseMiddleware(req, res, next) {
const url = req.url!;
-
+ if (manySlashes.test(url)) {
+ const destination = url.replace(manySlashes, '/');
+ return writeRedirectResponse(res, 301, destination);
+ }
let pathname: string;
try {
pathname = decodeURI(new URL(url, 'http://localhost').pathname);
diff --git a/packages/astro/src/vite-plugin-astro-server/response.ts b/packages/astro/src/vite-plugin-astro-server/response.ts
index ef3c8247aa..753e77c66c 100644
--- a/packages/astro/src/vite-plugin-astro-server/response.ts
+++ b/packages/astro/src/vite-plugin-astro-server/response.ts
@@ -7,6 +7,7 @@ import { Readable } from 'node:stream';
import { getSetCookiesFromResponse } from '../core/cookies/index.js';
import { getViteErrorPayload } from '../core/errors/dev/index.js';
import notFoundTemplate from '../template/4xx.js';
+import { redirectTemplate } from '../core/routing/3xx.js';
export async function handle404Response(
origin: string,
@@ -53,6 +54,17 @@ export function writeHtmlResponse(res: http.ServerResponse, statusCode: number,
res.end();
}
+export function writeRedirectResponse(res: http.ServerResponse, statusCode: number, location: string) {
+ const html = redirectTemplate({ status: statusCode, location });
+ res.writeHead(statusCode, {
+ Location: location,
+ 'Content-Type': 'text/html',
+ 'Content-Length': Buffer.byteLength(html, 'utf-8'),
+ });
+ res.write(html);
+ res.end();
+}
+
export async function writeWebResponse(res: http.ServerResponse, webResponse: Response) {
const { status, headers, body, statusText } = webResponse;
diff --git a/packages/astro/test/dev-routing.test.js b/packages/astro/test/dev-routing.test.js
index c6a19fc4e4..a43561769a 100644
--- a/packages/astro/test/dev-routing.test.js
+++ b/packages/astro/test/dev-routing.test.js
@@ -48,6 +48,19 @@ describe('Development Routing', () => {
assert.equal(response.status, 200);
});
+ it('redirects when loading double slash', async () => {
+ const response = await fixture.fetch('//', { redirect: 'manual' });
+ assert.equal(response.status, 301);
+ assert.equal(response.headers.get('Location'), '/');
+ });
+
+ it('redirects when loading multiple slashes', async () => {
+ const response = await fixture.fetch('/////', { redirect: 'manual' });
+ assert.equal(response.status, 301);
+ assert.equal(response.headers.get('Location'), '/');
+ });
+
+
it('404 when loading invalid dynamic route', async () => {
const response = await fixture.fetch('/2');
assert.equal(response.status, 404);