mirror of
https://github.com/withastro/astro.git
synced 2025-02-17 22:44:24 -05:00
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>
This commit is contained in:
parent
9b0a624c8c
commit
bbf1d8894e
5 changed files with 39 additions and 4 deletions
5
.changeset/twenty-cherries-switch.md
Normal file
5
.changeset/twenty-cherries-switch.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixes a bug that caused the dev server to return an error if requesting "//"
|
|
@ -1,5 +1,5 @@
|
||||||
export type RedirectTemplate = {
|
export type RedirectTemplate = {
|
||||||
from: string;
|
from?: string;
|
||||||
location: string | URL;
|
location: string | URL;
|
||||||
status: number;
|
status: number;
|
||||||
};
|
};
|
||||||
|
@ -14,6 +14,6 @@ export function redirectTemplate({ status, location, from }: RedirectTemplate) {
|
||||||
<meta name="robots" content="noindex">
|
<meta name="robots" content="noindex">
|
||||||
<link rel="canonical" href="${location}">
|
<link rel="canonical" href="${location}">
|
||||||
<body>
|
<body>
|
||||||
<a href="${location}">Redirecting from <code>${from}</code> to <code>${location}</code></a>
|
<a href="${location}">Redirecting ${from ? `from <code>${from}</code> ` : ''}to <code>${location}</code></a>
|
||||||
</body>`;
|
</body>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,9 @@ import { appendForwardSlash } from '@astrojs/internal-helpers/path';
|
||||||
import { bold } from 'kleur/colors';
|
import { bold } from 'kleur/colors';
|
||||||
import type { Logger } from '../core/logger/core.js';
|
import type { Logger } from '../core/logger/core.js';
|
||||||
import notFoundTemplate, { subpathNotUsedTemplate } from '../template/4xx.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(
|
export function baseMiddleware(
|
||||||
settings: AstroSettings,
|
settings: AstroSettings,
|
||||||
|
@ -21,7 +23,10 @@ export function baseMiddleware(
|
||||||
|
|
||||||
return function devBaseMiddleware(req, res, next) {
|
return function devBaseMiddleware(req, res, next) {
|
||||||
const url = req.url!;
|
const url = req.url!;
|
||||||
|
if (manySlashes.test(url)) {
|
||||||
|
const destination = url.replace(manySlashes, '/');
|
||||||
|
return writeRedirectResponse(res, 301, destination);
|
||||||
|
}
|
||||||
let pathname: string;
|
let pathname: string;
|
||||||
try {
|
try {
|
||||||
pathname = decodeURI(new URL(url, 'http://localhost').pathname);
|
pathname = decodeURI(new URL(url, 'http://localhost').pathname);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Readable } from 'node:stream';
|
||||||
import { getSetCookiesFromResponse } from '../core/cookies/index.js';
|
import { getSetCookiesFromResponse } from '../core/cookies/index.js';
|
||||||
import { getViteErrorPayload } from '../core/errors/dev/index.js';
|
import { getViteErrorPayload } from '../core/errors/dev/index.js';
|
||||||
import notFoundTemplate from '../template/4xx.js';
|
import notFoundTemplate from '../template/4xx.js';
|
||||||
|
import { redirectTemplate } from '../core/routing/3xx.js';
|
||||||
|
|
||||||
export async function handle404Response(
|
export async function handle404Response(
|
||||||
origin: string,
|
origin: string,
|
||||||
|
@ -53,6 +54,17 @@ export function writeHtmlResponse(res: http.ServerResponse, statusCode: number,
|
||||||
res.end();
|
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) {
|
export async function writeWebResponse(res: http.ServerResponse, webResponse: Response) {
|
||||||
const { status, headers, body, statusText } = webResponse;
|
const { status, headers, body, statusText } = webResponse;
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,19 @@ describe('Development Routing', () => {
|
||||||
assert.equal(response.status, 200);
|
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 () => {
|
it('404 when loading invalid dynamic route', async () => {
|
||||||
const response = await fixture.fetch('/2');
|
const response = await fixture.fetch('/2');
|
||||||
assert.equal(response.status, 404);
|
assert.equal(response.status, 404);
|
||||||
|
|
Loading…
Add table
Reference in a new issue