summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Kettmeir <crowlkats@toaxl.com>2023-06-13 04:15:08 +0200
committerGitHub <noreply@github.com>2023-06-13 04:15:08 +0200
commitb4ae37a617e5e2a1c248d0d1ac66dcead11e04cd (patch)
tree7d604b5dcc37f054e1de37eb50afe1cb3571a9de
parentd2c638464d108b945440429cfba0594c20089d76 (diff)
feat(node): HTTPS server (#19362)
-rw-r--r--cli/tests/unit_node/http_test.ts32
-rw-r--r--ext/node/polyfills/http.ts36
-rw-r--r--ext/node/polyfills/https.ts45
3 files changed, 97 insertions, 16 deletions
diff --git a/cli/tests/unit_node/http_test.ts b/cli/tests/unit_node/http_test.ts
index 8f87b1fd2..0d15bf889 100644
--- a/cli/tests/unit_node/http_test.ts
+++ b/cli/tests/unit_node/http_test.ts
@@ -6,6 +6,7 @@ import https from "node:https";
import {
assert,
assertEquals,
+ fail,
} from "../../../test_util/std/testing/asserts.ts";
import { assertSpyCalls, spy } from "../../../test_util/std/testing/mock.ts";
import { deferred } from "../../../test_util/std/async/deferred.ts";
@@ -617,3 +618,34 @@ Deno.test("[node/http] ClientRequest search params", async () => {
await def;
assertEquals(body, "foo=bar");
});
+
+Deno.test("[node/http] HTTPS server", async () => {
+ const promise = deferred<void>();
+ const promise2 = deferred<void>();
+ const client = Deno.createHttpClient({
+ caCerts: [Deno.readTextFileSync("cli/tests/testdata/tls/RootCA.pem")],
+ });
+ const server = https.createServer({
+ cert: Deno.readTextFileSync("cli/tests/testdata/tls/localhost.crt"),
+ key: Deno.readTextFileSync("cli/tests/testdata/tls/localhost.key"),
+ }, (_req, res) => {
+ res.end("success!");
+ });
+ server.listen(() => {
+ // deno-lint-ignore no-explicit-any
+ fetch(`https://localhost:${(server.address() as any).port}`, {
+ client,
+ }).then(async (res) => {
+ assertEquals(res.status, 200);
+ assertEquals(await res.text(), "success!");
+ server.close();
+ promise2.resolve();
+ });
+ })
+ .on("error", () => fail());
+ server.on("close", () => {
+ promise.resolve();
+ });
+ await Promise.all([promise, promise2]);
+ client.close();
+});
diff --git a/ext/node/polyfills/http.ts b/ext/node/polyfills/http.ts
index 697de6414..a207f57ce 100644
--- a/ext/node/polyfills/http.ts
+++ b/ext/node/polyfills/http.ts
@@ -18,6 +18,7 @@ import { nextTick } from "ext:deno_node/_next_tick.ts";
import {
validateBoolean,
validateInteger,
+ validateObject,
validatePort,
} from "ext:deno_node/internal/validators.mjs";
import {
@@ -1443,16 +1444,16 @@ export class IncomingMessageForServer extends NodeReadable {
}
}
-type ServerHandler = (
+export type ServerHandler = (
req: IncomingMessageForServer,
res: ServerResponse,
) => void;
-export function Server(handler?: ServerHandler): ServerImpl {
- return new ServerImpl(handler);
+export function Server(opts, requestListener?: ServerHandler): ServerImpl {
+ return new ServerImpl(opts, requestListener);
}
-class ServerImpl extends EventEmitter {
+export class ServerImpl extends EventEmitter {
#httpConnections: Set<Deno.HttpConn> = new Set();
#listener?: Deno.Listener;
@@ -1464,12 +1465,24 @@ class ServerImpl extends EventEmitter {
#servePromise: Deferred<void>;
listening = false;
- constructor(handler?: ServerHandler) {
+ constructor(opts, requestListener?: ServerHandler) {
super();
+
+ if (typeof opts === "function") {
+ requestListener = opts;
+ opts = kEmptyObject;
+ } else if (opts == null) {
+ opts = kEmptyObject;
+ } else {
+ validateObject(opts, "options");
+ }
+
+ this._opts = opts;
+
this.#servePromise = deferred();
this.#servePromise.then(() => this.emit("close"));
- if (handler !== undefined) {
- this.on("request", handler);
+ if (requestListener !== undefined) {
+ this.on("request", requestListener);
}
}
@@ -1498,12 +1511,12 @@ class ServerImpl extends EventEmitter {
port,
} as Deno.NetAddr;
this.listening = true;
- nextTick(() => this.#serve());
+ nextTick(() => this._serve());
return this;
}
- #serve() {
+ _serve() {
const ac = new AbortController();
const handler = (request: Request, info: Deno.ServeHandlerInfo) => {
const req = new IncomingMessageForServer(request, info.remoteAddr);
@@ -1536,6 +1549,7 @@ class ServerImpl extends EventEmitter {
this.#addr!.port = port;
this.emit("listening");
},
+ ...this._additionalServeOptions?.(),
},
);
if (this.#unref) {
@@ -1598,8 +1612,8 @@ class ServerImpl extends EventEmitter {
Server.prototype = ServerImpl.prototype;
-export function createServer(handler?: ServerHandler) {
- return Server(handler);
+export function createServer(opts, requestListener?: ServerHandler) {
+ return Server(opts, requestListener);
}
/** Makes an HTTP request. */
diff --git a/ext/node/polyfills/https.ts b/ext/node/polyfills/https.ts
index dfd8f24d9..b0b800416 100644
--- a/ext/node/polyfills/https.ts
+++ b/ext/node/polyfills/https.ts
@@ -10,14 +10,49 @@ import {
} from "ext:deno_node/http.ts";
import { Agent as HttpAgent } from "ext:deno_node/_http_agent.mjs";
import { createHttpClient } from "ext:deno_fetch/22_http_client.js";
+import {
+ type ServerHandler,
+ ServerImpl as HttpServer,
+} from "ext:deno_node/http.ts";
+import { validateObject } from "ext:deno_node/internal/validators.mjs";
+import { kEmptyObject } from "ext:deno_node/internal/util.mjs";
+import { Buffer } from "ext:deno_node/buffer.ts";
+
+export class Server extends HttpServer {
+ constructor(opts, requestListener?: ServerHandler) {
+ if (typeof opts === "function") {
+ requestListener = opts;
+ opts = kEmptyObject;
+ } else if (opts == null) {
+ opts = kEmptyObject;
+ } else {
+ validateObject(opts, "options");
+ }
+
+ if (opts.cert && Array.isArray(opts.cert)) {
+ notImplemented("https.Server.opts.cert array type");
+ }
-export class Server {
- constructor() {
- notImplemented("https.Server.prototype.constructor");
+ if (opts.key && Array.isArray(opts.key)) {
+ notImplemented("https.Server.opts.key array type");
+ }
+
+ super(opts, requestListener);
+ }
+
+ _additionalServeOptions() {
+ return {
+ cert: this._opts.cert instanceof Buffer
+ ? this._opts.cert.toString()
+ : this._opts.cert,
+ key: this._opts.key instanceof Buffer
+ ? this._opts.key.toString()
+ : this._opts.key,
+ };
}
}
-export function createServer() {
- notImplemented("https.createServer");
+export function createServer(opts, requestListener?: ServerHandler) {
+ return new Server(opts, requestListener);
}
interface HttpsRequestOptions extends RequestOptions {