2024-01-01 14:58:21 -05:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2023-02-14 11:38:45 -05:00
|
|
|
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
|
|
|
|
2023-03-08 06:44:54 -05:00
|
|
|
import { notImplemented } from "ext:deno_node/_utils.ts";
|
|
|
|
import { urlToHttpOptions } from "ext:deno_node/internal/url.ts";
|
2023-02-14 11:38:45 -05:00
|
|
|
import {
|
|
|
|
ClientRequest,
|
|
|
|
IncomingMessageForClient as IncomingMessage,
|
|
|
|
type RequestOptions,
|
2023-07-02 14:19:30 -04:00
|
|
|
} from "node:http";
|
2024-09-11 20:52:08 -04:00
|
|
|
import { Agent as HttpAgent } from "node:_http_agent";
|
2023-05-22 21:03:10 -04:00
|
|
|
import { createHttpClient } from "ext:deno_fetch/22_http_client.js";
|
2024-09-18 09:54:52 -04:00
|
|
|
import { type ServerHandler, ServerImpl as HttpServer } from "node:http";
|
2023-06-12 22:15:08 -04:00
|
|
|
import { validateObject } from "ext:deno_node/internal/validators.mjs";
|
|
|
|
import { kEmptyObject } from "ext:deno_node/internal/util.mjs";
|
2023-07-02 14:19:30 -04:00
|
|
|
import { Buffer } from "node:buffer";
|
2024-10-21 03:50:53 -04:00
|
|
|
import { primordials } from "ext:core/mod.js";
|
|
|
|
const {
|
|
|
|
ArrayPrototypeShift,
|
|
|
|
ArrayPrototypeUnshift,
|
|
|
|
ArrayIsArray,
|
|
|
|
ObjectPrototypeIsPrototypeOf,
|
|
|
|
ObjectAssign,
|
|
|
|
} = primordials;
|
2023-06-12 22:15:08 -04:00
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2024-10-21 03:50:53 -04:00
|
|
|
if (opts.cert && ArrayIsArray(opts.cert)) {
|
2023-06-12 22:15:08 -04:00
|
|
|
notImplemented("https.Server.opts.cert array type");
|
|
|
|
}
|
|
|
|
|
2024-10-21 03:50:53 -04:00
|
|
|
if (opts.key && ArrayIsArray(opts.key)) {
|
2023-06-12 22:15:08 -04:00
|
|
|
notImplemented("https.Server.opts.key array type");
|
|
|
|
}
|
|
|
|
|
|
|
|
super(opts, requestListener);
|
|
|
|
}
|
2023-02-14 11:38:45 -05:00
|
|
|
|
2023-06-12 22:15:08 -04:00
|
|
|
_additionalServeOptions() {
|
|
|
|
return {
|
2024-10-21 03:50:53 -04:00
|
|
|
cert: ObjectPrototypeIsPrototypeOf(Buffer, this._opts.cert)
|
|
|
|
// deno-lint-ignore prefer-primordials
|
2023-06-12 22:15:08 -04:00
|
|
|
? this._opts.cert.toString()
|
|
|
|
: this._opts.cert,
|
2024-10-21 03:50:53 -04:00
|
|
|
key: ObjectPrototypeIsPrototypeOf(Buffer, this._opts.key)
|
|
|
|
// deno-lint-ignore prefer-primordials
|
2023-06-12 22:15:08 -04:00
|
|
|
? this._opts.key.toString()
|
|
|
|
: this._opts.key,
|
|
|
|
};
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
2023-07-20 20:18:07 -04:00
|
|
|
|
|
|
|
_encrypted = true;
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
2023-06-12 22:15:08 -04:00
|
|
|
export function createServer(opts, requestListener?: ServerHandler) {
|
|
|
|
return new Server(opts, requestListener);
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
interface HttpsRequestOptions extends RequestOptions {
|
|
|
|
_: unknown;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store additional root CAs.
|
|
|
|
// undefined means NODE_EXTRA_CA_CERTS is not checked yet.
|
|
|
|
// null means there's no additional root CAs.
|
|
|
|
let caCerts: string[] | undefined | null;
|
|
|
|
|
|
|
|
/** Makes a request to an https server. */
|
|
|
|
export function get(
|
|
|
|
url: string | URL,
|
|
|
|
cb?: (res: IncomingMessage) => void,
|
|
|
|
): HttpsClientRequest;
|
|
|
|
export function get(
|
|
|
|
opts: HttpsRequestOptions,
|
|
|
|
cb?: (res: IncomingMessage) => void,
|
|
|
|
): HttpsClientRequest;
|
|
|
|
export function get(
|
|
|
|
url: string | URL,
|
|
|
|
opts: HttpsRequestOptions,
|
|
|
|
cb?: (res: IncomingMessage) => void,
|
|
|
|
): HttpsClientRequest;
|
|
|
|
// deno-lint-ignore no-explicit-any
|
|
|
|
export function get(...args: any[]) {
|
|
|
|
const req = request(args[0], args[1], args[2]);
|
|
|
|
req.end();
|
|
|
|
return req;
|
|
|
|
}
|
|
|
|
|
2023-05-16 19:20:32 -04:00
|
|
|
export class Agent extends HttpAgent {
|
|
|
|
constructor(options) {
|
|
|
|
super(options);
|
|
|
|
this.defaultPort = 443;
|
|
|
|
this.protocol = "https:";
|
|
|
|
this.maxCachedSessions = this.options.maxCachedSessions;
|
|
|
|
if (this.maxCachedSessions === undefined) {
|
|
|
|
this.maxCachedSessions = 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._sessionCache = {
|
|
|
|
map: {},
|
|
|
|
list: [],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-10 23:43:30 -05:00
|
|
|
export const globalAgent = new Agent({
|
2023-05-16 19:20:32 -04:00
|
|
|
keepAlive: true,
|
|
|
|
scheduling: "lifo",
|
|
|
|
timeout: 5000,
|
|
|
|
});
|
|
|
|
|
2023-02-14 11:38:45 -05:00
|
|
|
/** HttpsClientRequest class loosely follows http.ClientRequest class API. */
|
|
|
|
class HttpsClientRequest extends ClientRequest {
|
2023-07-20 20:18:07 -04:00
|
|
|
override _encrypted: true;
|
2023-02-14 11:38:45 -05:00
|
|
|
override defaultProtocol = "https:";
|
2023-05-16 19:20:32 -04:00
|
|
|
override _getClient(): Deno.HttpClient | undefined {
|
2023-02-14 11:38:45 -05:00
|
|
|
if (caCerts === null) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
if (caCerts !== undefined) {
|
2023-05-22 21:03:10 -04:00
|
|
|
return createHttpClient({ caCerts, http2: false });
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
2023-05-16 19:20:32 -04:00
|
|
|
// const status = await Deno.permissions.query({
|
|
|
|
// name: "env",
|
|
|
|
// variable: "NODE_EXTRA_CA_CERTS",
|
|
|
|
// });
|
|
|
|
// if (status.state !== "granted") {
|
|
|
|
// caCerts = null;
|
|
|
|
// return undefined;
|
|
|
|
// }
|
2023-02-14 11:38:45 -05:00
|
|
|
const certFilename = Deno.env.get("NODE_EXTRA_CA_CERTS");
|
|
|
|
if (!certFilename) {
|
|
|
|
caCerts = null;
|
|
|
|
return undefined;
|
|
|
|
}
|
2023-05-16 19:20:32 -04:00
|
|
|
const caCert = Deno.readTextFileSync(certFilename);
|
2023-02-14 11:38:45 -05:00
|
|
|
caCerts = [caCert];
|
2023-05-22 21:03:10 -04:00
|
|
|
return createHttpClient({ caCerts, http2: false });
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Makes a request to an https server. */
|
|
|
|
export function request(
|
|
|
|
url: string | URL,
|
|
|
|
cb?: (res: IncomingMessage) => void,
|
|
|
|
): HttpsClientRequest;
|
|
|
|
export function request(
|
|
|
|
opts: HttpsRequestOptions,
|
|
|
|
cb?: (res: IncomingMessage) => void,
|
|
|
|
): HttpsClientRequest;
|
|
|
|
export function request(
|
|
|
|
url: string | URL,
|
|
|
|
opts: HttpsRequestOptions,
|
|
|
|
cb?: (res: IncomingMessage) => void,
|
|
|
|
): HttpsClientRequest;
|
|
|
|
// deno-lint-ignore no-explicit-any
|
|
|
|
export function request(...args: any[]) {
|
|
|
|
let options = {};
|
2023-05-16 19:20:32 -04:00
|
|
|
|
2023-02-14 11:38:45 -05:00
|
|
|
if (typeof args[0] === "string") {
|
2024-10-21 03:50:53 -04:00
|
|
|
const urlStr = ArrayPrototypeShift(args);
|
2023-05-16 19:20:32 -04:00
|
|
|
options = urlToHttpOptions(new URL(urlStr));
|
2024-10-21 03:50:53 -04:00
|
|
|
} else if (ObjectPrototypeIsPrototypeOf(URL, args[0])) {
|
|
|
|
options = urlToHttpOptions(ArrayPrototypeShift(args));
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
2023-05-16 19:20:32 -04:00
|
|
|
|
2023-02-14 11:38:45 -05:00
|
|
|
if (args[0] && typeof args[0] !== "function") {
|
2024-10-21 03:50:53 -04:00
|
|
|
ObjectAssign(options, ArrayPrototypeShift(args));
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
2023-05-16 19:20:32 -04:00
|
|
|
|
|
|
|
options._defaultAgent = globalAgent;
|
2024-10-21 03:50:53 -04:00
|
|
|
ArrayPrototypeUnshift(args, options);
|
2023-05-16 19:20:32 -04:00
|
|
|
|
2023-02-14 11:38:45 -05:00
|
|
|
return new HttpsClientRequest(args[0], args[1]);
|
|
|
|
}
|
|
|
|
export default {
|
|
|
|
Agent,
|
|
|
|
Server,
|
|
|
|
createServer,
|
|
|
|
get,
|
|
|
|
globalAgent,
|
|
|
|
request,
|
|
|
|
};
|