mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 00:29:09 -05:00
feat(node): HTTPS server (#19362)
This commit is contained in:
parent
2fdbea763e
commit
28b04b285e
3 changed files with 97 additions and 16 deletions
|
@ -6,6 +6,7 @@ import https from "node:https";
|
||||||
import {
|
import {
|
||||||
assert,
|
assert,
|
||||||
assertEquals,
|
assertEquals,
|
||||||
|
fail,
|
||||||
} from "../../../test_util/std/testing/asserts.ts";
|
} from "../../../test_util/std/testing/asserts.ts";
|
||||||
import { assertSpyCalls, spy } from "../../../test_util/std/testing/mock.ts";
|
import { assertSpyCalls, spy } from "../../../test_util/std/testing/mock.ts";
|
||||||
import { deferred } from "../../../test_util/std/async/deferred.ts";
|
import { deferred } from "../../../test_util/std/async/deferred.ts";
|
||||||
|
@ -617,3 +618,34 @@ Deno.test("[node/http] ClientRequest search params", async () => {
|
||||||
await def;
|
await def;
|
||||||
assertEquals(body, "foo=bar");
|
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();
|
||||||
|
});
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { nextTick } from "ext:deno_node/_next_tick.ts";
|
||||||
import {
|
import {
|
||||||
validateBoolean,
|
validateBoolean,
|
||||||
validateInteger,
|
validateInteger,
|
||||||
|
validateObject,
|
||||||
validatePort,
|
validatePort,
|
||||||
} from "ext:deno_node/internal/validators.mjs";
|
} from "ext:deno_node/internal/validators.mjs";
|
||||||
import {
|
import {
|
||||||
|
@ -1443,16 +1444,16 @@ export class IncomingMessageForServer extends NodeReadable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerHandler = (
|
export type ServerHandler = (
|
||||||
req: IncomingMessageForServer,
|
req: IncomingMessageForServer,
|
||||||
res: ServerResponse,
|
res: ServerResponse,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
export function Server(handler?: ServerHandler): ServerImpl {
|
export function Server(opts, requestListener?: ServerHandler): ServerImpl {
|
||||||
return new ServerImpl(handler);
|
return new ServerImpl(opts, requestListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerImpl extends EventEmitter {
|
export class ServerImpl extends EventEmitter {
|
||||||
#httpConnections: Set<Deno.HttpConn> = new Set();
|
#httpConnections: Set<Deno.HttpConn> = new Set();
|
||||||
#listener?: Deno.Listener;
|
#listener?: Deno.Listener;
|
||||||
|
|
||||||
|
@ -1464,12 +1465,24 @@ class ServerImpl extends EventEmitter {
|
||||||
#servePromise: Deferred<void>;
|
#servePromise: Deferred<void>;
|
||||||
listening = false;
|
listening = false;
|
||||||
|
|
||||||
constructor(handler?: ServerHandler) {
|
constructor(opts, requestListener?: ServerHandler) {
|
||||||
super();
|
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 = deferred();
|
||||||
this.#servePromise.then(() => this.emit("close"));
|
this.#servePromise.then(() => this.emit("close"));
|
||||||
if (handler !== undefined) {
|
if (requestListener !== undefined) {
|
||||||
this.on("request", handler);
|
this.on("request", requestListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1498,12 +1511,12 @@ class ServerImpl extends EventEmitter {
|
||||||
port,
|
port,
|
||||||
} as Deno.NetAddr;
|
} as Deno.NetAddr;
|
||||||
this.listening = true;
|
this.listening = true;
|
||||||
nextTick(() => this.#serve());
|
nextTick(() => this._serve());
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
#serve() {
|
_serve() {
|
||||||
const ac = new AbortController();
|
const ac = new AbortController();
|
||||||
const handler = (request: Request, info: Deno.ServeHandlerInfo) => {
|
const handler = (request: Request, info: Deno.ServeHandlerInfo) => {
|
||||||
const req = new IncomingMessageForServer(request, info.remoteAddr);
|
const req = new IncomingMessageForServer(request, info.remoteAddr);
|
||||||
|
@ -1536,6 +1549,7 @@ class ServerImpl extends EventEmitter {
|
||||||
this.#addr!.port = port;
|
this.#addr!.port = port;
|
||||||
this.emit("listening");
|
this.emit("listening");
|
||||||
},
|
},
|
||||||
|
...this._additionalServeOptions?.(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (this.#unref) {
|
if (this.#unref) {
|
||||||
|
@ -1598,8 +1612,8 @@ class ServerImpl extends EventEmitter {
|
||||||
|
|
||||||
Server.prototype = ServerImpl.prototype;
|
Server.prototype = ServerImpl.prototype;
|
||||||
|
|
||||||
export function createServer(handler?: ServerHandler) {
|
export function createServer(opts, requestListener?: ServerHandler) {
|
||||||
return Server(handler);
|
return Server(opts, requestListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Makes an HTTP request. */
|
/** Makes an HTTP request. */
|
||||||
|
|
|
@ -10,14 +10,49 @@ import {
|
||||||
} from "ext:deno_node/http.ts";
|
} from "ext:deno_node/http.ts";
|
||||||
import { Agent as HttpAgent } from "ext:deno_node/_http_agent.mjs";
|
import { Agent as HttpAgent } from "ext:deno_node/_http_agent.mjs";
|
||||||
import { createHttpClient } from "ext:deno_fetch/22_http_client.js";
|
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 {
|
export class Server extends HttpServer {
|
||||||
constructor() {
|
constructor(opts, requestListener?: ServerHandler) {
|
||||||
notImplemented("https.Server.prototype.constructor");
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
export function createServer(opts, requestListener?: ServerHandler) {
|
||||||
notImplemented("https.createServer");
|
return new Server(opts, requestListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HttpsRequestOptions extends RequestOptions {
|
interface HttpsRequestOptions extends RequestOptions {
|
||||||
|
|
Loading…
Reference in a new issue