mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
feat(std): added TLS serve abilities to file_server (#6962)
This commit is contained in:
parent
b344a7f81f
commit
91ff91093d
2 changed files with 164 additions and 36 deletions
111
std/http/file_server.ts
Executable file → Normal file
111
std/http/file_server.ts
Executable file → Normal file
|
@ -7,7 +7,13 @@
|
|||
// https://github.com/indexzero/http-server/blob/master/test/http-server-test.js
|
||||
|
||||
import { posix, extname } from "../path/mod.ts";
|
||||
import { listenAndServe, ServerRequest, Response } from "./server.ts";
|
||||
import {
|
||||
listenAndServe,
|
||||
listenAndServeTLS,
|
||||
ServerRequest,
|
||||
Response,
|
||||
HTTPSOptions,
|
||||
} from "./server.ts";
|
||||
import { parse } from "../flags/mod.ts";
|
||||
import { assert } from "../_util/assert.ts";
|
||||
|
||||
|
@ -28,6 +34,14 @@ interface FileServerArgs {
|
|||
// -h --help
|
||||
h: boolean;
|
||||
help: boolean;
|
||||
// --host
|
||||
host: string;
|
||||
// -c --cert
|
||||
c: string;
|
||||
cert: string;
|
||||
// -k --key
|
||||
k: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
@ -49,6 +63,7 @@ const MEDIA_TYPES: Record<string, string> = {
|
|||
".gz": "application/gzip",
|
||||
".css": "text/css",
|
||||
".wasm": "application/wasm",
|
||||
".mjs": "application/javascript",
|
||||
};
|
||||
|
||||
/** Returns the content-type based on the extension of a path. */
|
||||
|
@ -306,7 +321,19 @@ function html(strings: TemplateStringsArray, ...values: unknown[]): string {
|
|||
|
||||
function main(): void {
|
||||
const CORSEnabled = serverArgs.cors ? true : false;
|
||||
const addr = `0.0.0.0:${serverArgs.port ?? serverArgs.p ?? 4507}`;
|
||||
const port = serverArgs.port ?? serverArgs.p ?? 4507;
|
||||
const host = serverArgs.host ?? "0.0.0.0";
|
||||
const addr = `${host}:${port}`;
|
||||
const tlsOpts = {} as HTTPSOptions;
|
||||
tlsOpts.certFile = serverArgs.cert ?? serverArgs.c ?? "";
|
||||
tlsOpts.keyFile = serverArgs.key ?? serverArgs.k ?? "";
|
||||
|
||||
if (tlsOpts.keyFile || tlsOpts.certFile) {
|
||||
if (tlsOpts.keyFile === "" || tlsOpts.certFile === "") {
|
||||
console.log("--key and --cert are required for TLS");
|
||||
serverArgs.h = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (serverArgs.h ?? serverArgs.help) {
|
||||
console.log(`Deno File Server
|
||||
|
@ -321,50 +348,62 @@ function main(): void {
|
|||
OPTIONS:
|
||||
-h, --help Prints help information
|
||||
-p, --port <PORT> Set port
|
||||
--cors Enable CORS via the "Access-Control-Allow-Origin" header`);
|
||||
--cors Enable CORS via the "Access-Control-Allow-Origin" header
|
||||
--host <HOST> Hostname (default is 0.0.0.0)
|
||||
-c, --cert <FILE> TLS certificate file (enables TLS)
|
||||
-k, --key <FILE> TLS key file (enables TLS)
|
||||
|
||||
All TLS options are required when one is provided.`);
|
||||
|
||||
Deno.exit();
|
||||
}
|
||||
|
||||
listenAndServe(
|
||||
addr,
|
||||
async (req): Promise<void> => {
|
||||
let normalizedUrl = posix.normalize(req.url);
|
||||
try {
|
||||
normalizedUrl = decodeURIComponent(normalizedUrl);
|
||||
} catch (e) {
|
||||
if (!(e instanceof URIError)) {
|
||||
throw e;
|
||||
}
|
||||
const handler = async (req: ServerRequest): Promise<void> => {
|
||||
let normalizedUrl = posix.normalize(req.url);
|
||||
try {
|
||||
normalizedUrl = decodeURIComponent(normalizedUrl);
|
||||
} catch (e) {
|
||||
if (!(e instanceof URIError)) {
|
||||
throw e;
|
||||
}
|
||||
const fsPath = posix.join(target, normalizedUrl);
|
||||
}
|
||||
const fsPath = posix.join(target, normalizedUrl);
|
||||
|
||||
let response: Response | undefined;
|
||||
let response: Response | undefined;
|
||||
try {
|
||||
const fileInfo = await Deno.stat(fsPath);
|
||||
if (fileInfo.isDirectory) {
|
||||
response = await serveDir(req, fsPath);
|
||||
} else {
|
||||
response = await serveFile(req, fsPath);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
response = await serveFallback(req, e);
|
||||
} finally {
|
||||
if (CORSEnabled) {
|
||||
assert(response);
|
||||
setCORS(response);
|
||||
}
|
||||
serverLog(req, response!);
|
||||
try {
|
||||
const fileInfo = await Deno.stat(fsPath);
|
||||
if (fileInfo.isDirectory) {
|
||||
response = await serveDir(req, fsPath);
|
||||
} else {
|
||||
response = await serveFile(req, fsPath);
|
||||
}
|
||||
await req.respond(response!);
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
response = await serveFallback(req, e);
|
||||
} finally {
|
||||
if (CORSEnabled) {
|
||||
assert(response);
|
||||
setCORS(response);
|
||||
}
|
||||
serverLog(req, response!);
|
||||
try {
|
||||
await req.respond(response!);
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
console.log(`HTTP server listening on http://${addr}/`);
|
||||
let proto = "http";
|
||||
if (tlsOpts.keyFile || tlsOpts.certFile) {
|
||||
proto += "s";
|
||||
tlsOpts.hostname = host;
|
||||
tlsOpts.port = port;
|
||||
listenAndServeTLS(tlsOpts, handler);
|
||||
} else {
|
||||
listenAndServe(addr, handler);
|
||||
}
|
||||
console.log(`${proto.toUpperCase()} server listening on ${proto}://${addr}/`);
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
|
|
|
@ -199,3 +199,92 @@ Deno.test("file_server running as library", async function (): Promise<void> {
|
|||
await killFileServer();
|
||||
}
|
||||
});
|
||||
|
||||
async function startTlsFileServer({
|
||||
target = ".",
|
||||
port = 4577,
|
||||
}: FileServerCfg = {}): Promise<void> {
|
||||
fileServer = Deno.run({
|
||||
cmd: [
|
||||
Deno.execPath(),
|
||||
"run",
|
||||
"--allow-read",
|
||||
"--allow-net",
|
||||
"http/file_server.ts",
|
||||
target,
|
||||
"--host",
|
||||
"localhost",
|
||||
"--cert",
|
||||
"./http/testdata/tls/localhost.crt",
|
||||
"--key",
|
||||
"./http/testdata/tls/localhost.key",
|
||||
"--cors",
|
||||
"-p",
|
||||
`${port}`,
|
||||
],
|
||||
stdout: "piped",
|
||||
stderr: "null",
|
||||
});
|
||||
// Once fileServer is ready it will write to its stdout.
|
||||
assert(fileServer.stdout != null);
|
||||
const r = new TextProtoReader(new BufReader(fileServer.stdout));
|
||||
const s = await r.readLine();
|
||||
assert(s !== null && s.includes("server listening"));
|
||||
}
|
||||
|
||||
Deno.test("serveDirectory TLS", async function (): Promise<void> {
|
||||
await startTlsFileServer();
|
||||
try {
|
||||
// Valid request after invalid
|
||||
const conn = await Deno.connectTls({
|
||||
hostname: "localhost",
|
||||
port: 4577,
|
||||
certFile: "./http/testdata/tls/RootCA.pem",
|
||||
});
|
||||
|
||||
await Deno.writeAll(
|
||||
conn,
|
||||
new TextEncoder().encode("GET /http HTTP/1.0\r\n\r\n"),
|
||||
);
|
||||
const res = new Uint8Array(128 * 1024);
|
||||
const nread = await conn.read(res);
|
||||
assert(nread !== null);
|
||||
conn.close();
|
||||
const page = new TextDecoder().decode(res.subarray(0, nread));
|
||||
assert(page.includes("<title>Deno File Server</title>"));
|
||||
} finally {
|
||||
await killFileServer();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("partial TLS arguments fail", async function (): Promise<void> {
|
||||
fileServer = Deno.run({
|
||||
cmd: [
|
||||
Deno.execPath(),
|
||||
"run",
|
||||
"--allow-read",
|
||||
"--allow-net",
|
||||
"http/file_server.ts",
|
||||
".",
|
||||
"--host",
|
||||
"localhost",
|
||||
"--cert",
|
||||
"./http/testdata/tls/localhost.crt",
|
||||
"-p",
|
||||
`4578`,
|
||||
],
|
||||
stdout: "piped",
|
||||
stderr: "null",
|
||||
});
|
||||
try {
|
||||
// Once fileServer is ready it will write to its stdout.
|
||||
assert(fileServer.stdout != null);
|
||||
const r = new TextProtoReader(new BufReader(fileServer.stdout));
|
||||
const s = await r.readLine();
|
||||
assert(
|
||||
s !== null && s.includes("--key and --cert are required for TLS"),
|
||||
);
|
||||
} finally {
|
||||
await killFileServer();
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue