summaryrefslogtreecommitdiff
path: root/std
diff options
context:
space:
mode:
Diffstat (limited to 'std')
-rwxr-xr-xstd/http/file_server.ts195
-rw-r--r--std/http/file_server_test.ts10
-rw-r--r--std/http/testdata/simple_https_server.ts6
3 files changed, 129 insertions, 82 deletions
diff --git a/std/http/file_server.ts b/std/http/file_server.ts
index 8dc2ee87a..fb8f28081 100755
--- a/std/http/file_server.ts
+++ b/std/http/file_server.ts
@@ -7,8 +7,7 @@
// https://github.com/indexzero/http-server/blob/master/test/http-server-test.js
const { ErrorKind, cwd, args, stat, readDir, open } = Deno;
-import { contentType } from "../media_types/mod.ts";
-import { extname, posix } from "../path/mod.ts";
+import { posix } from "../path/mod.ts";
import {
listenAndServe,
ServerRequest,
@@ -16,33 +15,14 @@ import {
Response
} from "./server.ts";
-const dirViewerTemplate = `
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>Deno File Server</title>
- <style>
- td {
- padding: 0 1rem;
- }
- td.mode {
- font-family: Courier;
- }
- </style>
-</head>
-<body>
- <h1>Index of <%DIRNAME%></h1>
- <table>
- <tr><th>Mode</th><th>Size</th><th>Name</th></tr>
- <%CONTENTS%>
- </table>
-</body>
-</html>
-`;
+interface EntryInfo {
+ mode: string;
+ size: string;
+ url: string;
+ name: string;
+}
+const encoder = new TextEncoder();
const serverArgs = args.slice();
let CORSEnabled = false;
// TODO: switch to flags if we later want to add more options
@@ -58,7 +38,6 @@ const target = posix.isAbsolute(targetArg)
? posix.normalize(targetArg)
: posix.join(cwd(), targetArg);
const addr = `0.0.0.0:${serverArgs[2] || 4500}`;
-const encoder = new TextEncoder();
function modeToString(isDir: boolean, maybeMode: number | null): string {
const modeMap = ["---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"];
@@ -99,25 +78,6 @@ function fileLenToString(len: number): string {
return `${(len / base).toFixed(2)}${suffix[suffixIndex]}`;
}
-function createDirEntryDisplay(
- name: string,
- url: string,
- size: number | null,
- mode: number | null,
- isDir: boolean
-): string {
- const sizeStr = size === null ? "" : "" + fileLenToString(size!);
- return `
- <tr><td class="mode">${modeToString(
- isDir,
- mode
- )}</td><td>${sizeStr}</td><td><a href="${url}">${name}${
- isDir ? "/" : ""
- }</a></td>
- </tr>
- `;
-}
-
async function serveFile(
req: ServerRequest,
filePath: string
@@ -126,7 +86,7 @@ async function serveFile(
const fileInfo = await stat(filePath);
const headers = new Headers();
headers.set("content-length", fileInfo.len.toString());
- headers.set("content-type", contentType(extname(filePath)) || "text/plain");
+ headers.set("content-type", "text/plain");
const res = {
status: 200,
@@ -141,12 +101,8 @@ async function serveDir(
req: ServerRequest,
dirPath: string
): Promise<Response> {
- interface ListItem {
- name: string;
- template: string;
- }
const dirUrl = `/${posix.relative(target, dirPath)}`;
- const listEntry: ListItem[] = [];
+ const listEntry: EntryInfo[] = [];
const fileInfos = await readDir(dirPath);
for (const fileInfo of fileInfos) {
const filePath = posix.join(dirPath, fileInfo.name);
@@ -161,29 +117,17 @@ async function serveDir(
mode = (await stat(filePath)).mode;
} catch (e) {}
listEntry.push({
+ mode: modeToString(fileInfo.isDirectory(), mode),
+ size: fileInfo.isFile() ? fileLenToString(fileInfo.len) : "",
name: fileInfo.name,
- template: createDirEntryDisplay(
- fileInfo.name,
- fileUrl,
- fileInfo.isFile() ? fileInfo.len : null,
- mode,
- fileInfo.isDirectory()
- )
+ url: fileUrl
});
}
-
- const formattedDirUrl = `${dirUrl.replace(/\/$/, "")}/`;
- const page = new TextEncoder().encode(
- dirViewerTemplate.replace("<%DIRNAME%>", formattedDirUrl).replace(
- "<%CONTENTS%>",
- listEntry
- .sort((a, b): number =>
- a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1
- )
- .map((v): string => v.template)
- .join("")
- )
+ listEntry.sort((a, b) =>
+ a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1
);
+ const formattedDirUrl = `${dirUrl.replace(/\/$/, "")}/`;
+ const page = encoder.encode(dirViewerTemplate(formattedDirUrl, listEntry));
const headers = new Headers();
headers.set("content-type", "text/html");
@@ -232,6 +176,111 @@ function setCORS(res: Response): void {
);
}
+function dirViewerTemplate(dirname: string, entries: EntryInfo[]): string {
+ return html`
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="X-UA-Compatible" content="ie=edge" />
+ <title>Deno File Server</title>
+ <style>
+ :root {
+ --background-color: #fafafa;
+ --color: rgba(0, 0, 0, 0.87);
+ }
+ @media (prefers-color-scheme: dark) {
+ :root {
+ --background-color: #303030;
+ --color: #fff;
+ }
+ }
+ @media (min-width: 960px) {
+ main {
+ max-width: 960px;
+ }
+ body {
+ padding-left: 32px;
+ padding-right: 32px;
+ }
+ }
+ @media (min-width: 600px) {
+ main {
+ padding-left: 24px;
+ padding-right: 24px;
+ }
+ }
+ body {
+ background: var(--background-color);
+ color: var(--color);
+ font-family: "Roboto", "Helvetica", "Arial", sans-serif;
+ font-weight: 400;
+ line-height: 1.43;
+ font-size: 0.875rem;
+ }
+ a {
+ color: #2196f3;
+ text-decoration: none;
+ }
+ a:hover {
+ text-decoration: underline;
+ }
+ table th {
+ text-align: left;
+ }
+ table td {
+ padding: 12px 24px 0 0;
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <h1>Index of ${dirname}</h1>
+ <table>
+ <tr>
+ <th>Mode</th>
+ <th>Size</th>
+ <th>Name</th>
+ </tr>
+ ${entries.map(
+ entry => html`
+ <tr>
+ <td class="mode">
+ ${entry.mode}
+ </td>
+ <td>
+ ${entry.size}
+ </td>
+ <td>
+ <a href="${entry.url}">${entry.name}</a>
+ </td>
+ </tr>
+ `
+ )}
+ </table>
+ </main>
+ </body>
+ </html>
+ `;
+}
+
+function html(strings: TemplateStringsArray, ...values: unknown[]): string {
+ const l = strings.length - 1;
+ let html = "";
+
+ for (let i = 0; i < l; i++) {
+ let v = values[i];
+ if (v instanceof Array) {
+ v = v.join("");
+ }
+ const s = strings[i] + v;
+ html += s;
+ }
+ html += strings[l];
+ return html;
+}
+
listenAndServe(
addr,
async (req): Promise<void> => {
diff --git a/std/http/file_server_test.ts b/std/http/file_server_test.ts
index 77467b8c8..7d7e024e7 100644
--- a/std/http/file_server_test.ts
+++ b/std/http/file_server_test.ts
@@ -36,10 +36,6 @@ test(async function serveFile(): Promise<void> {
const res = await fetch("http://localhost:4500/README.md");
assert(res.headers.has("access-control-allow-origin"));
assert(res.headers.has("access-control-allow-headers"));
- assertEquals(
- res.headers.get("content-type"),
- "text/markdown; charset=utf-8"
- );
const downloadedFile = await res.text();
const localFile = new TextDecoder().decode(
await Deno.readFile("README.md")
@@ -63,10 +59,10 @@ test(async function serveDirectory(): Promise<void> {
// TODO: `mode` should work correctly in the future.
// Correct this test case accordingly.
Deno.build.os !== "win" &&
- assert(/<td class="mode">\([a-zA-Z-]{10}\)<\/td>/.test(page));
+ assert(/<td class="mode">(\s)*\([a-zA-Z-]{10}\)(\s)*<\/td>/.test(page));
Deno.build.os === "win" &&
- assert(/<td class="mode">\(unknown mode\)<\/td>/.test(page));
- assert(page.includes(`<td><a href="/README.md">README.md</a></td>`));
+ assert(/<td class="mode">(\s)*\(unknown mode\)(\s)*<\/td>/.test(page));
+ assert(page.includes(`<a href="/README.md">README.md</a>`));
} finally {
killFileServer();
}
diff --git a/std/http/testdata/simple_https_server.ts b/std/http/testdata/simple_https_server.ts
index 655457c94..21d1181cf 100644
--- a/std/http/testdata/simple_https_server.ts
+++ b/std/http/testdata/simple_https_server.ts
@@ -6,10 +6,12 @@ const tlsOptions = {
hostname: "localhost",
port: 4503,
certFile: "./http/testdata/tls/localhost.crt",
- keyFile: "./http/testdata/tls/localhost.key",
+ keyFile: "./http/testdata/tls/localhost.key"
};
const s = serveTLS(tlsOptions);
-console.log(`Simple HTTPS server listening on ${tlsOptions.hostname}:${tlsOptions.port}`);
+console.log(
+ `Simple HTTPS server listening on ${tlsOptions.hostname}:${tlsOptions.port}`
+);
const body = new TextEncoder().encode("Hello HTTPS");
for await (const req of s) {
req.respond({ body });