Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ describe('createServeRequestHandler', () => {
/missing 404 404.html
/missing/ 404 404.html
/missing.html 301 /missing → 404 404.html
/redirect-folder.html 301 /redirect-folder → 404 404.html
/redirect-folder.html 200 redirect-folder.html/index.html
/redirect-folder.html/ 200 redirect-folder.html/index.html
/redirect-file.html 301 /redirect-file → 404 404.html
/redirect-file.html 200 redirect-file.html.html
/redirect-file.html/ 200 redirect-file.html.html
/file?query=1 200 file.html
/file/?query=1 200 file.html
Expand Down Expand Up @@ -219,9 +219,9 @@ describe('createServeRequestHandler', () => {
/missing 301 /missing/ → 404 404.html
/missing/ 404 404.html
/missing.html 301 /missing → 301 /missing/ → 404 404.html
/redirect-folder.html 301 /redirect-folder → 301 /redirect-folder/ → 404 404.html
/redirect-folder.html 301 /redirect-folder.html/ → 200 redirect-folder.html/index.html
/redirect-folder.html/ 200 redirect-folder.html/index.html
/redirect-file.html 301 /redirect-file → 301 /redirect-file/ → 404 404.html
/redirect-file.html 301 /redirect-file.html/ → 200 redirect-file.html.html
/redirect-file.html/ 200 redirect-file.html.html
/file?query=1 301 /file/?query=1 → 200 file.html
/file/?query=1 200 file.html
Expand Down Expand Up @@ -283,10 +283,10 @@ describe('createServeRequestHandler', () => {
/missing 404 404.html
/missing/ 301 /missing → 404 404.html
/missing.html 301 /missing → 404 404.html
/redirect-folder.html 301 /redirect-folder → 404 404.html
/redirect-folder.html/ 301 /redirect-folder.html → 301 /redirect-folder → 404 404.html
/redirect-file.html 301 /redirect-file → 404 404.html
/redirect-file.html/ 301 /redirect-file.html → 301 /redirect-file → 404 404.html
/redirect-folder.html 200 redirect-folder.html/index.html
/redirect-folder.html/ 301 /redirect-folder.html → 200 redirect-folder.html/index.html
/redirect-file.html 200 redirect-file.html.html
/redirect-file.html/ 301 /redirect-file.html → 200 redirect-file.html.html
/file?query=1 200 file.html
/file/?query=1 301 /file?query=1 → 200 file.html
/file.html?query=1 200 file.html
Expand Down Expand Up @@ -349,9 +349,9 @@ describe('createServeRequestHandler', () => {
/base/missing 404 404.html
/base/missing/ 404 404.html
/base/missing.html 301 /base/missing → 404 404.html
/base/redirect-folder.html 301 /base/redirect-folder → 404 404.html
/base/redirect-folder.html 200 redirect-folder.html/index.html
/base/redirect-folder.html/ 200 redirect-folder.html/index.html
/base/redirect-file.html 301 /base/redirect-file → 404 404.html
/base/redirect-file.html 200 redirect-file.html.html
/base/redirect-file.html/ 200 redirect-file.html.html
/base/file?query=1 200 file.html
/base/file/?query=1 200 file.html
Expand Down Expand Up @@ -413,9 +413,9 @@ describe('createServeRequestHandler', () => {
/base/missing 301 /base/missing/ → 404 404.html
/base/missing/ 404 404.html
/base/missing.html 301 /base/missing → 301 /base/missing/ → 404 404.html
/base/redirect-folder.html 301 /base/redirect-folder → 301 /base/redirect-folder/ → 404 404.html
/base/redirect-folder.html 301 /base/redirect-folder.html/ → 200 redirect-folder.html/index.html
/base/redirect-folder.html/ 200 redirect-folder.html/index.html
/base/redirect-file.html 301 /base/redirect-file → 301 /base/redirect-file/ → 404 404.html
/base/redirect-file.html 301 /base/redirect-file.html/ → 200 redirect-file.html.html
/base/redirect-file.html/ 200 redirect-file.html.html
/base/file?query=1 301 /base/file/?query=1 → 200 file.html
/base/file/?query=1 200 file.html
Expand Down Expand Up @@ -477,10 +477,10 @@ describe('createServeRequestHandler', () => {
/base/missing 404 404.html
/base/missing/ 301 /base/missing → 404 404.html
/base/missing.html 301 /base/missing → 404 404.html
/base/redirect-folder.html 301 /base/redirect-folder → 404 404.html
/base/redirect-folder.html/ 301 /base/redirect-folder.html → 301 /base/redirect-folder → 404 404.html
/base/redirect-file.html 301 /base/redirect-file → 404 404.html
/base/redirect-file.html/ 301 /base/redirect-file.html → 301 /base/redirect-file → 404 404.html
/base/redirect-folder.html 200 redirect-folder.html/index.html
/base/redirect-folder.html/ 301 /base/redirect-folder.html → 200 redirect-folder.html/index.html
/base/redirect-file.html 200 redirect-file.html.html
/base/redirect-file.html/ 301 /base/redirect-file.html → 200 redirect-file.html.html
/base/file?query=1 200 file.html
/base/file/?query=1 301 /base/file?query=1 → 200 file.html
/base/file.html?query=1 200 file.html
Expand Down Expand Up @@ -837,27 +837,25 @@ describe('createServeRequestHandler', () => {
);
});

// TODO fix: "200 redirect-folder.html/index.html"
// plugin-client-redirects writes it for redirects from /redirect-folder.html
it('serves no client redirect folder of .html paths', async () => {
it('serves the client redirect folder of .html paths', async () => {
await expect(
serveRequest(
{baseUrl: '/base/', trailingSlash: undefined},
'/base/redirect-folder.html',
),
).resolves.toBe('301 /base/redirect-folder → 404 404.html');
).resolves.toBe('200 redirect-folder.html/index.html');
});

// TODO fix: "200 redirect-file.html.html"
// plugin-client-redirects writes it for redirects from /redirect-file.html,
// with trailingSlash false
it('serves no client redirect file of .html paths', async () => {
it('serves the client redirect file of .html paths', async () => {
await expect(
serveRequest(
{baseUrl: '/base/', trailingSlash: false},
'/base/redirect-file.html',
),
).resolves.toBe('301 /base/redirect-file → 404 404.html');
).resolves.toBe('200 redirect-file.html.html');
});

// Like the malformed percent-encodings
Expand Down
33 changes: 30 additions & 3 deletions packages/docusaurus/src/commands/serve/requestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ export function createServeRequestHandler({
// Requests without url were redirected to the baseUrl
req.url = req.url!.replace(baseUrl, '/');

// serve-handler can't serve these pages: cleanUrls would redirect them
req.url = (await getHtmlPathPageFileUrl(outDir, req.url)) ?? req.url;

await serveHandler(req, res, {
// cleanUrls serves /file with file.html, but also redirects /file.html
// to /file, even when the file exists, and drops the baseUrl
Expand Down Expand Up @@ -139,12 +142,15 @@ async function getRedirectLocation(
}

// Static hosts serve the existing .html files, without clean URL redirects
if (await isHtmlFileRequest(outDir, url.slice(baseUrl.length - 1))) {
const outDirUrl = url.slice(baseUrl.length - 1);
if (await isHtmlFileRequest(outDir, outDirUrl)) {
return undefined;
}

const isHtmlPathPage =
(await getHtmlPathPageFileUrl(outDir, outDirUrl)) !== undefined;
const cleanPathname = getCleanPathname(pathname, baseUrl);
if (pathname !== cleanPathname) {
if (!isHtmlPathPage && pathname !== cleanPathname) {
return serializeURLPath({pathname: cleanPathname, search});
}

Expand Down Expand Up @@ -197,6 +203,27 @@ function hasNullByte(url: string): boolean {
return pathname.includes('%00');
}

// The page of a .html path that isn't a file, like the client redirect files
// "old-page.html/index.html", or "old-page.html.html" with trailingSlash false
// See https://github.com/meta/docusaurus/pull/12920
async function getHtmlPathPageFileUrl(
outDir: string,
url: string,
): Promise<string | undefined> {
const pathname = url.split(/[?#]/)[0]!;
if (!pathname.endsWith('.html')) {
return undefined;
}
const queryAndHash = url.slice(pathname.length);
const pageFileUrls = [`${pathname}/index.html`, `${pathname}.html`];
for (const pageFileUrl of pageFileUrls) {
if (await isHtmlFileRequest(outDir, pageFileUrl)) {
return pageFileUrl + queryAndHash;
}
}
return undefined;
}

// The decoded paths that serve-handler's cleanUrls redirects
function hasCleanUrlSuffix(url: string): boolean {
const pathname = url.split(/[?#]/)[0]!;
Expand Down Expand Up @@ -231,7 +258,7 @@ async function isHtmlFileRequest(
if (!filePath?.endsWith('.html')) {
return false;
}
// Not a folder, like the client redirect file "legacy.html/index.html"
// Not a folder, like the client redirect file "old-page.html/index.html"
const stats = await fs.stat(filePath).catch(() => undefined);
return stats?.isFile() ?? false;
}
Expand Down
Loading