feat(http/file_server): add showIndex option to serveDir (#2738)

This commit is contained in:
Yoshiya Hinosawa 2022-10-03 20:09:09 +09:00 committed by GitHub
parent 3a582ee373
commit b161285109
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 56 additions and 13 deletions

View File

@ -282,12 +282,10 @@ export async function serveFile(
// TODO(bartlomieju): simplify this after deno.stat and deno.readDir are fixed
async function serveDirIndex(
req: Request,
dirPath: string,
options: {
dotfiles: boolean;
target: string;
etagAlgorithm?: EtagAlgorithm;
},
): Promise<Response> {
const showDotfiles = options.dotfiles;
@ -314,13 +312,6 @@ async function serveDirIndex(
const fileUrl = encodeURIComponent(posix.join(dirUrl, entry.name))
.replaceAll("%2F", "/");
const fileInfo = await Deno.stat(filePath);
if (entry.name === "index.html" && entry.isFile) {
// in case index.html as dir...
return serveFile(req, filePath, {
etagAlgorithm: options.etagAlgorithm,
fileInfo,
});
}
listEntry.push({
mode: modeToString(entry.isDirectory, fileInfo.mode),
size: entry.isFile ? fileLenToString(fileInfo.size ?? 0) : "",
@ -515,6 +506,8 @@ export interface ServeDirOptions {
showDirListing?: boolean;
/** Serves dotfiles. Defaults to false. */
showDotfiles?: boolean;
/** Serves index.html as the index file of the directory. */
showIndex?: boolean;
/** Enable CORS via the "Access-Control-Allow-Origin" header. Defaults to false. */
enableCors?: boolean;
/** Do not print request level logs. Defaults to false. Defaults to false. */
@ -562,14 +555,16 @@ export interface ServeDirOptions {
* @param opts.urlRoot Specified that part is stripped from the beginning of the requested pathname.
* @param opts.showDirListing Enable directory listing. Defaults to false.
* @param opts.showDotfiles Serves dotfiles. Defaults to false.
* @param opts.showIndex Serves index.html as the index file of the directory.
* @param opts.enableCors Enable CORS via the "Access-Control-Allow-Origin" header. Defaults to false.
* @param opts.quiet Do not print request level logs. Defaults to false.
* @param opts.etagAlgorithm Etag The algorithm to use for generating the ETag. Defaults to "fnv1a".
*/
export async function serveDir(req: Request, opts: ServeDirOptions = {}) {
let response: Response;
let response: Response | undefined = undefined;
const target = opts.fsRoot || ".";
const urlRoot = opts.urlRoot;
const showIndex = opts.showIndex ?? true;
try {
let normalizedPath = normalizeURL(req.url);
@ -585,12 +580,30 @@ export async function serveDir(req: Request, opts: ServeDirOptions = {}) {
const fileInfo = await Deno.stat(fsPath);
if (fileInfo.isDirectory) {
if (opts.showDirListing) {
response = await serveDirIndex(req, fsPath, {
if (showIndex) {
try {
const path = posix.join(fsPath, "index.html");
const indexFileInfo = await Deno.lstat(path);
if (indexFileInfo.isFile) {
response = await serveFile(req, path, {
etagAlgorithm: opts.etagAlgorithm,
fileInfo: indexFileInfo,
});
}
} catch (e) {
if (!(e instanceof Deno.errors.NotFound)) {
throw e;
}
// pass
}
}
if (!response && opts.showDirListing) {
response = await serveDirIndex(fsPath, {
dotfiles: opts.showDotfiles || false,
target,
});
} else {
}
if (!response) {
throw new Deno.errors.NotFound();
}
} else {

View File

@ -1046,6 +1046,35 @@ Deno.test(
},
);
Deno.test(
"serveDir serves index.html when showIndex is true",
async () => {
const url = "http://localhost:4507/http/testdata/subdir-with-index/";
const expectedText = "This is subdir-with-index/index.html";
{
const res = await serveDir(new Request(url), { showIndex: true });
assertEquals(res.status, 200);
assertStringIncludes(await res.text(), expectedText);
}
{
// showIndex is true by default
const res = await serveDir(new Request(url));
assertEquals(res.status, 200);
assertStringIncludes(await res.text(), expectedText);
}
},
);
Deno.test(
"serveDir doesn't serve index.html when showIndex is false",
async () => {
const url = "http://localhost:4507/http/testdata/subdir-with-index/";
const res = await serveDir(new Request(url), { showIndex: false });
assertEquals(res.status, 404);
},
);
Deno.test(
"file_server returns 304 for requests with if-none-match set with the etag but with W/ prefixed etag in request headers.",
async () => {

View File

@ -0,0 +1 @@
This is subdir-with-index/index.html