Files
bun.sh/test/js/node/tls/node-tls-server.test.ts
Ciro Spaciari 14832c5547 fix(CI) update cert in harness (#22440)
### What does this PR do?
update harness.ts
### How did you verify your code works?
CI

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-09-05 20:42:25 -07:00

689 lines
19 KiB
TypeScript

import { readFileSync, realpathSync } from "fs";
import { tls as cert1 } from "harness";
import { AddressInfo } from "net";
import { createTest } from "node-harness";
import { once } from "node:events";
import { tmpdir } from "os";
import { join } from "path";
import type { PeerCertificate } from "tls";
import tls, { connect, createServer, rootCertificates, Server, TLSSocket } from "tls";
const { describe, expect, it, createCallCheckCtx } = createTest(import.meta.path);
const passKeyFile = join(import.meta.dir, "fixtures", "rsa_private_encrypted.pem");
const passKey = readFileSync(passKeyFile);
const rawKeyFile = join(import.meta.dir, "fixtures", "rsa_private.pem");
const rawKey = readFileSync(rawKeyFile);
const certFile = join(import.meta.dir, "fixtures", "rsa_cert.crt");
const cert = readFileSync(certFile);
const COMMON_CERT = { ...cert1 };
const socket_domain = join(realpathSync(tmpdir()), "node-tls-server.sock");
describe("tls.createServer listen", () => {
it("should throw when no port or path when using options", done => {
expect(() => createServer(COMMON_CERT).listen({ exclusive: true })).toThrow(
'The argument \'options\' must have the property "port" or "path". Received {"exclusive":true}',
);
done();
});
it("should listen on IPv6 by default", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer(COMMON_CERT);
let timeout: Timer;
const closeAndFail = () => {
clearTimeout(timeout);
server.close();
mustNotCall()();
};
server.on("error", closeAndFail);
timeout = setTimeout(closeAndFail, 100);
server.listen(
0,
mustCall(() => {
const address = server.address() as AddressInfo;
expect(address.address).toStrictEqual("::");
//system should provide an port when 0 or no port is passed
expect(address.port).toBeGreaterThan(100);
expect(address.family).toStrictEqual("IPv6");
server.close();
done();
}),
);
});
it("should listen on IPv4", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer(COMMON_CERT);
let timeout: Timer;
const closeAndFail = () => {
clearTimeout(timeout);
server.close();
mustNotCall()();
};
server.on("error", closeAndFail);
timeout = setTimeout(closeAndFail, 100);
server.listen(
0,
"0.0.0.0",
mustCall(() => {
const address = server.address() as AddressInfo;
expect(address.address).toStrictEqual("0.0.0.0");
//system should provide an port when 0 or no port is passed
expect(address.port).toBeGreaterThan(100);
expect(address.family).toStrictEqual("IPv4");
server.close();
done();
}),
);
});
it("should call listening", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer(COMMON_CERT);
let timeout: Timer;
const closeAndFail = () => {
clearTimeout(timeout);
server.close();
mustNotCall()();
};
server.on("error", closeAndFail).on(
"listening",
mustCall(() => {
clearTimeout(timeout);
server.close();
done();
}),
);
timeout = setTimeout(closeAndFail, 100);
server.listen(0, "0.0.0.0");
});
it("should listen on localhost", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer(COMMON_CERT);
let timeout: Timer;
const closeAndFail = () => {
clearTimeout(timeout);
server.close();
mustNotCall()();
};
server.on("error", closeAndFail);
timeout = setTimeout(closeAndFail, 100);
server.listen(
0,
"::1",
mustCall(() => {
const address = server.address() as AddressInfo;
expect(address.address).toStrictEqual("::1");
//system should provide an port when 0 or no port is passed
expect(address.port).toBeGreaterThan(100);
expect(address.family).toStrictEqual("IPv6");
server.close();
done();
}),
);
});
it("should listen on localhost", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer(COMMON_CERT);
let timeout: Timer;
const closeAndFail = () => {
clearTimeout(timeout);
server.close();
mustNotCall()();
};
server.on("error", closeAndFail);
timeout = setTimeout(closeAndFail, 100);
server.listen(
0,
"::1",
mustCall(() => {
const address = server.address() as AddressInfo;
expect(address.address).toStrictEqual("::1");
expect(address.family).toStrictEqual("IPv6");
server.close();
done();
}),
);
});
it("should listen without port or host", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer(COMMON_CERT);
let timeout: Timer;
const closeAndFail = () => {
clearTimeout(timeout);
server.close();
mustNotCall()();
};
server.on("error", closeAndFail);
timeout = setTimeout(closeAndFail, 100);
server.listen(
mustCall(() => {
const address = server.address() as AddressInfo;
expect(address.address).toStrictEqual("::");
//system should provide an port when 0 or no port is passed
expect(address.port).toBeGreaterThan(100);
expect(address.family).toStrictEqual("IPv6");
server.close();
done();
}),
);
});
it("should listen on unix domain socket", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer(COMMON_CERT);
let timeout: Timer;
const closeAndFail = () => {
clearTimeout(timeout);
server.close();
mustNotCall()();
};
server.on("error", closeAndFail);
timeout = setTimeout(closeAndFail, 100);
server.listen(
socket_domain,
mustCall(() => {
const address = server.address();
expect(address).toStrictEqual(socket_domain);
server.close();
done();
}),
);
});
it("should not listen with wrong password", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer({
key: passKey,
passphrase: "invalid",
cert: cert,
});
server.on("error", mustCall());
let timeout: Timer;
function closeAndFail() {
clearTimeout(timeout);
server.close();
mustNotCall()();
}
timeout = setTimeout(closeAndFail, 100);
server.listen(0, "0.0.0.0", closeAndFail);
});
it("should not listen without cert", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer({
key: passKey,
passphrase: "invalid",
});
server.on("error", mustCall());
let timeout: Timer;
function closeAndFail() {
clearTimeout(timeout);
server.close();
mustNotCall()();
}
timeout = setTimeout(closeAndFail, 100);
server.listen(0, "0.0.0.0", closeAndFail);
});
it("should not listen without password", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer({
key: passKey,
cert: cert,
});
server.on("error", mustCall());
let timeout: Timer;
function closeAndFail() {
clearTimeout(timeout);
server.close();
mustNotCall()();
}
timeout = setTimeout(closeAndFail, 100);
server.listen(0, "0.0.0.0", closeAndFail);
});
});
describe("tls.createServer", () => {
it("should work with getCertificate", done => {
let timeout: Timer;
let client: TLSSocket | null = null;
const server: Server = createServer(COMMON_CERT, socket => {
socket.on("secure", () => {
try {
expect(socket).toBeDefined();
const cert = socket.getCertificate() as PeerCertificate;
expect(cert).toBeDefined();
expect(cert.subject).toBeDefined();
expect(cert.subject).toMatchObject({
C: "US",
CN: "server-bun",
L: "San Francisco",
O: "Oven",
OU: "Team Bun",
ST: "CA",
});
expect(cert.issuer).toBeDefined();
expect(cert.issuer).toMatchObject({
C: "US",
CN: "server-bun",
L: "San Francisco",
O: "Oven",
OU: "Team Bun",
ST: "CA",
});
expect(cert.ca).toBe(true);
expect(cert.bits).toBe(2048);
expect(cert.modulus).toBe(
"e5633a2c8118171cbeaf321d55d0444586cbe566bb51a234b0ead69faf7490069854efddffac68986652ff949f472252e4c7d24c6ee4e3366e54d9e4701e24d021e583e1a088112c0f96475a558b42f883a3e796c937cc4d6bb8791b227017b3e73deb40b0ac84f033019f580a3216888acec71ce52d938fcadd8e29794e38774e33d323ede89b58e526ef8b513ba465fa4ffd9cf6c1ec7480de0dcb569dec295d7b3cce40256b428d5907e90e7a52e77c3101f4ad4c0e254ab03d75ac42ee1668a5094bc4521b264fb404b6c4b17b6b279e13e6282e1e4fb6303540cb830ea8ff576ca57b7861e4ef797af824b0987c870718780a1c5141e4f904fd0c5139f5",
);
expect(cert.exponent).toBe("0x10001");
expect(cert.pubkey).toBeInstanceOf(Buffer);
// yes these spaces are intentional
expect(cert.valid_from).toBe("Sep 6 03:00:49 2025 GMT");
expect(cert.valid_to).toBe("Sep 4 03:00:49 2035 GMT");
expect(cert.fingerprint).toBe("D2:5E:B9:AD:8B:48:3B:7A:35:D3:1A:45:BD:32:AC:AD:55:4A:BA:AD");
expect(cert.fingerprint256).toBe(
"85:F4:47:0C:6D:D8:DE:C8:68:77:7C:5E:3F:9B:56:A6:D3:69:C7:C2:1A:E8:B8:F8:1C:16:1D:04:78:A0:E9:91",
);
expect(cert.fingerprint512).toBe(
"CE:00:17:97:29:5E:1C:7E:59:86:8D:1F:F0:F4:AF:A0:B0:10:F2:2E:0E:79:D1:32:D0:44:F9:B4:3A:DE:D5:83:A9:15:0E:E4:47:24:D4:2A:10:FB:21:BE:3A:38:21:FC:40:20:B3:BC:52:64:F7:38:93:EF:C9:3F:C8:57:89:31",
);
expect(cert.serialNumber).toBe("71a46ae89fd817ef81a34d5973e1de42f09b9d63");
expect(cert.raw).toBeInstanceOf(Buffer);
client?.end();
server.close();
done();
} catch (err) {
client?.end();
server.close();
done(err);
}
});
});
const closeAndFail = (err: any) => {
clearTimeout(timeout);
server.close();
client?.end();
done(err || "Timeout");
};
server.on("error", closeAndFail);
timeout = setTimeout(closeAndFail, 1000);
server.listen(0, () => {
const address = server.address() as AddressInfo;
client = connect({
port: address.port,
host: address.address,
secureContext: tls.createSecureContext(COMMON_CERT),
rejectUnauthorized: false,
});
});
});
});
describe("tls.createServer events", () => {
it("should receive data", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
let timeout: Timer;
let client: any = null;
let is_done = false;
const onData = mustCall(data => {
is_done = true;
clearTimeout(timeout);
server.close();
expect(data.byteLength).toBe(5);
expect(data.toString("utf8")).toBe("Hello");
done();
});
const server: Server = createServer(COMMON_CERT, (socket: TLSSocket) => {
socket.on("data", onData);
});
const closeAndFail = () => {
if (is_done) return;
clearTimeout(timeout);
server.close();
client?.end();
mustNotCall("no data received")();
};
server.on("error", closeAndFail);
//should be faster than 100ms
timeout = setTimeout(closeAndFail, 100);
server.listen(
mustCall(async () => {
const address = server.address() as AddressInfo;
client = await Bun.connect({
tls: true,
hostname: address.address,
port: address.port,
socket: {
data(socket) {},
handshake(socket, success, verifyError) {
if (socket.write("Hello")) {
socket.end();
}
},
connectError: closeAndFail, // connection failed
},
}).catch(closeAndFail);
}),
);
});
it("should call end", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
let timeout: Timer;
let is_done = false;
const onEnd = mustCall(() => {
is_done = true;
clearTimeout(timeout);
server.close();
done();
});
const server: Server = createServer(COMMON_CERT, (socket: TLSSocket) => {
socket.on("end", onEnd);
socket.end();
});
const closeAndFail = () => {
if (is_done) return;
clearTimeout(timeout);
server.close();
mustNotCall("end not called")();
};
server.on("error", closeAndFail);
//should be faster than 100ms
timeout = setTimeout(closeAndFail, 100);
server.listen(
mustCall(async () => {
const address = server.address() as AddressInfo;
await Bun.connect({
tls: true,
hostname: address.address,
port: address.port,
socket: {
data(socket) {},
open(socket) {},
connectError: closeAndFail, // connection failed
},
}).catch(closeAndFail);
}),
);
});
it("should call close", async () => {
const { promise, reject, resolve } = Promise.withResolvers();
const server: Server = createServer(COMMON_CERT);
server.listen().on("close", resolve).on("error", reject);
server.close();
await promise;
});
it("should call connection and drop", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
let timeout: Timer;
let is_done = false;
const server = createServer(COMMON_CERT);
let maxClients = 2;
server.maxConnections = maxClients - 1;
const closeAndFail = () => {
if (is_done) return;
clearTimeout(timeout);
server.close();
mustNotCall("drop not called")();
};
//should be faster than 100ms
timeout = setTimeout(closeAndFail, 100);
let connection_called = false;
server
.on(
"connection",
mustCall(() => {
connection_called = true;
}),
)
.on(
"drop",
mustCall(data => {
is_done = true;
server.close();
clearTimeout(timeout);
expect(data.localPort).toBeDefined();
expect(data.remotePort).toBeDefined();
expect(data.remoteFamily).toBeDefined();
expect(data.localFamily).toBeDefined();
expect(data.localAddress).toBeDefined();
expect(connection_called).toBe(true);
done();
}),
)
.listen(async () => {
const address = server.address() as AddressInfo;
async function spawnClient() {
await Bun.connect({
tls: true,
port: address?.port,
hostname: address?.address,
socket: {
data(socket) {},
handshake(socket, success, verifyError) {},
open(socket) {
socket.end();
},
},
});
}
const promises = [];
for (let i = 0; i < maxClients; i++) {
promises.push(spawnClient());
}
await Promise.all(promises).catch(closeAndFail);
});
});
it("should error on an invalid port", () => {
const server = createServer(COMMON_CERT);
expect(() => server.listen(123456)).toThrow(
expect.objectContaining({
code: "ERR_SOCKET_BAD_PORT",
}),
);
});
it("should call abort with signal", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const controller = new AbortController();
let timeout: Timer;
const server = createServer(COMMON_CERT);
const closeAndFail = () => {
clearTimeout(timeout);
server.close();
mustNotCall("close not called")();
};
//should be faster than 100ms
timeout = setTimeout(closeAndFail, 100);
server
.on(
"close",
mustCall(() => {
clearTimeout(timeout);
done();
}),
)
.listen({ port: 0, signal: controller.signal }, () => {
controller.abort();
});
});
it("should echo data", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
let timeout: Timer;
let client: any = null;
const server: Server = createServer(COMMON_CERT, (socket: TLSSocket) => {
socket.pipe(socket);
});
let is_done = false;
const closeAndFail = () => {
if (is_done) return;
clearTimeout(timeout);
server.close();
client?.end();
mustNotCall("no data received")();
};
server.on("error", closeAndFail);
//should be faster than 100ms
timeout = setTimeout(closeAndFail, 100);
server.listen(
mustCall(async () => {
const address = server.address() as AddressInfo;
client = await Bun.connect({
tls: true,
hostname: address.address,
port: address.port,
socket: {
error(socket, err) {
closeAndFail();
},
drain(socket) {
socket.write("Hello");
},
data(socket, data) {
is_done = true;
clearTimeout(timeout);
server.close();
socket.end();
expect(data.byteLength).toBe(5);
expect(data.toString("utf8")).toBe("Hello");
done();
},
handshake(socket) {
socket.write("Hello");
},
connectError: closeAndFail, // connection failed
},
}).catch(closeAndFail);
}),
);
});
});
it("tls.rootCertificates should exists", () => {
expect(tls.rootCertificates).toBeDefined();
expect(tls.rootCertificates).toBeInstanceOf(Array);
expect(tls.rootCertificates.length).toBeGreaterThan(0);
expect(typeof tls.rootCertificates[0]).toBe("string");
expect(rootCertificates).toBeDefined();
expect(rootCertificates).toBeInstanceOf(Array);
expect(rootCertificates.length).toBeGreaterThan(0);
expect(typeof rootCertificates[0]).toBe("string");
});
it("connectionListener should emit the right amount of times, and with alpnProtocol available", async () => {
let count = 0;
const promises = [];
const server: Server = createServer(
{
...COMMON_CERT,
ALPNProtocols: ["bun"],
},
socket => {
count++;
expect(socket.alpnProtocol).toBe("bun");
socket.end();
},
);
server.setMaxListeners(100);
server.listen(0);
await once(server, "listening");
for (let i = 0; i < 50; i++) {
const { promise, resolve } = Promise.withResolvers();
promises.push(promise);
const socket = connect(
{
ca: COMMON_CERT.cert,
rejectUnauthorized: false,
port: server.address().port,
host: "127.0.0.1",
ALPNProtocols: ["bun"],
},
() => {
socket.on("close", resolve);
socket.resume();
socket.end();
},
);
}
await Promise.all(promises);
expect(count).toBe(50);
});