Skip to content

Commit bbf1d88

Browse files
ascorbicematipico
andauthoredJan 16, 2025··
fix: handle requests for double slash in dev (#12733)
* fix: handle requests for double slash in dev * Handle base * Oops * Snapshots * Move redirect out of routing --------- Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
1 parent 9b0a624 commit bbf1d88

File tree

5 files changed

+39
-4
lines changed

5 files changed

+39
-4
lines changed
 

‎.changeset/twenty-cherries-switch.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fixes a bug that caused the dev server to return an error if requesting "//"

‎packages/astro/src/core/routing/3xx.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export type RedirectTemplate = {
2-
from: string;
2+
from?: string;
33
location: string | URL;
44
status: number;
55
};
@@ -14,6 +14,6 @@ export function redirectTemplate({ status, location, from }: RedirectTemplate) {
1414
<meta name="robots" content="noindex">
1515
<link rel="canonical" href="${location}">
1616
<body>
17-
<a href="${location}">Redirecting from <code>${from}</code> to <code>${location}</code></a>
17+
<a href="${location}">Redirecting ${from ? `from <code>${from}</code> ` : ''}to <code>${location}</code></a>
1818
</body>`;
1919
}

‎packages/astro/src/vite-plugin-astro-server/base.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import { appendForwardSlash } from '@astrojs/internal-helpers/path';
77
import { bold } from 'kleur/colors';
88
import type { Logger } from '../core/logger/core.js';
99
import notFoundTemplate, { subpathNotUsedTemplate } from '../template/4xx.js';
10-
import { writeHtmlResponse } from './response.js';
10+
import { writeHtmlResponse, writeRedirectResponse } from './response.js';
11+
12+
const manySlashes = /\/{2,}$/;
1113

1214
export function baseMiddleware(
1315
settings: AstroSettings,
@@ -21,7 +23,10 @@ export function baseMiddleware(
2123

2224
return function devBaseMiddleware(req, res, next) {
2325
const url = req.url!;
24-
26+
if (manySlashes.test(url)) {
27+
const destination = url.replace(manySlashes, '/');
28+
return writeRedirectResponse(res, 301, destination);
29+
}
2530
let pathname: string;
2631
try {
2732
pathname = decodeURI(new URL(url, 'http://localhost').pathname);

‎packages/astro/src/vite-plugin-astro-server/response.ts

+12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Readable } from 'node:stream';
77
import { getSetCookiesFromResponse } from '../core/cookies/index.js';
88
import { getViteErrorPayload } from '../core/errors/dev/index.js';
99
import notFoundTemplate from '../template/4xx.js';
10+
import { redirectTemplate } from '../core/routing/3xx.js';
1011

1112
export async function handle404Response(
1213
origin: string,
@@ -53,6 +54,17 @@ export function writeHtmlResponse(res: http.ServerResponse, statusCode: number,
5354
res.end();
5455
}
5556

57+
export function writeRedirectResponse(res: http.ServerResponse, statusCode: number, location: string) {
58+
const html = redirectTemplate({ status: statusCode, location });
59+
res.writeHead(statusCode, {
60+
Location: location,
61+
'Content-Type': 'text/html',
62+
'Content-Length': Buffer.byteLength(html, 'utf-8'),
63+
});
64+
res.write(html);
65+
res.end();
66+
}
67+
5668
export async function writeWebResponse(res: http.ServerResponse, webResponse: Response) {
5769
const { status, headers, body, statusText } = webResponse;
5870

‎packages/astro/test/dev-routing.test.js

+13
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ describe('Development Routing', () => {
4848
assert.equal(response.status, 200);
4949
});
5050

51+
it('redirects when loading double slash', async () => {
52+
const response = await fixture.fetch('//', { redirect: 'manual' });
53+
assert.equal(response.status, 301);
54+
assert.equal(response.headers.get('Location'), '/');
55+
});
56+
57+
it('redirects when loading multiple slashes', async () => {
58+
const response = await fixture.fetch('/////', { redirect: 'manual' });
59+
assert.equal(response.status, 301);
60+
assert.equal(response.headers.get('Location'), '/');
61+
});
62+
63+
5164
it('404 when loading invalid dynamic route', async () => {
5265
const response = await fixture.fetch('/2');
5366
assert.equal(response.status, 404);

0 commit comments

Comments
 (0)
Please sign in to comment.