diff options
Diffstat (limited to 'test/js/node/tls/node-tls-server.test.ts')
-rw-r--r-- | test/js/node/tls/node-tls-server.test.ts | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/test/js/node/tls/node-tls-server.test.ts b/test/js/node/tls/node-tls-server.test.ts new file mode 100644 index 000000000..a82f8cde2 --- /dev/null +++ b/test/js/node/tls/node-tls-server.test.ts @@ -0,0 +1,627 @@ +import { createServer, Server, TLSSocket } from "tls"; +import { realpathSync, readFileSync } from "fs"; +import { tmpdir } from "os"; +import { join } from "path"; +import { createTest } from "node-harness"; +import { AddressInfo } from "net"; + +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: object = { + cert: "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKLdQVPy90jjMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\nBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\naWRnaXRzIFB0eSBMdGQwHhcNMTkwMjAzMTQ0OTM1WhcNMjAwMjAzMTQ0OTM1WjBF\nMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\nZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEA7i7IIEdICTiSTVx+ma6xHxOtcbd6wGW3nkxlCkJ1UuV8NmY5ovMsGnGD\nhJJtUQ2j5ig5BcJUf3tezqCNW4tKnSOgSISfEAKvpn2BPvaFq3yx2Yjz0ruvcGKp\nDMZBXmB/AAtGyN/UFXzkrcfppmLHJTaBYGG6KnmU43gPkSDy4iw46CJFUOupc51A\nFIz7RsE7mbT1plCM8e75gfqaZSn2k+Wmy+8n1HGyYHhVISRVvPqkS7gVLSVEdTea\nUtKP1Vx/818/HDWk3oIvDVWI9CFH73elNxBkMH5zArSNIBTehdnehyAevjY4RaC/\nkK8rslO3e4EtJ9SnA4swOjCiqAIQEwIDAQABo1AwTjAdBgNVHQ4EFgQUv5rc9Smm\n9c4YnNf3hR49t4rH4yswHwYDVR0jBBgwFoAUv5rc9Smm9c4YnNf3hR49t4rH4ysw\nDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATcL9CAAXg0u//eYUAlQa\nL+l8yKHS1rsq1sdmx7pvsmfZ2g8ONQGfSF3TkzkI2OOnCBokeqAYuyT8awfdNUtE\nEHOihv4ZzhK2YZVuy0fHX2d4cCFeQpdxno7aN6B37qtsLIRZxkD8PU60Dfu9ea5F\nDDynnD0TUabna6a0iGn77yD8GPhjaJMOz3gMYjQFqsKL252isDVHEDbpVxIzxPmN\nw1+WK8zRNdunAcHikeoKCuAPvlZ83gDQHp07dYdbuZvHwGj0nfxBLc9qt90XsBtC\n4IYR7c/bcLMmKXYf0qoQ4OzngsnPI5M+v9QEHvYWaKVwFY4CTcSNJEwfXw+BAeO5\nOA==\n-----END CERTIFICATE-----", + key: "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDuLsggR0gJOJJN\nXH6ZrrEfE61xt3rAZbeeTGUKQnVS5Xw2Zjmi8ywacYOEkm1RDaPmKDkFwlR/e17O\noI1bi0qdI6BIhJ8QAq+mfYE+9oWrfLHZiPPSu69wYqkMxkFeYH8AC0bI39QVfOSt\nx+mmYsclNoFgYboqeZTjeA+RIPLiLDjoIkVQ66lznUAUjPtGwTuZtPWmUIzx7vmB\n+pplKfaT5abL7yfUcbJgeFUhJFW8+qRLuBUtJUR1N5pS0o/VXH/zXz8cNaTegi8N\nVYj0IUfvd6U3EGQwfnMCtI0gFN6F2d6HIB6+NjhFoL+QryuyU7d7gS0n1KcDizA6\nMKKoAhATAgMBAAECggEAd5g/3o1MK20fcP7PhsVDpHIR9faGCVNJto9vcI5cMMqP\n6xS7PgnSDFkRC6EmiLtLn8Z0k2K3YOeGfEP7lorDZVG9KoyE/doLbpK4MfBAwBG1\nj6AHpbmd5tVzQrnNmuDjBBelbDmPWVbD0EqAFI6mphXPMqD/hFJWIz1mu52Kt2s6\n++MkdqLO0ORDNhKmzu6SADQEcJ9Suhcmv8nccMmwCsIQAUrfg3qOyqU4//8QB8ZM\njosO3gMUesihVeuF5XpptFjrAliPgw9uIG0aQkhVbf/17qy0XRi8dkqXj3efxEDp\n1LSqZjBFiqJlFchbz19clwavMF/FhxHpKIhhmkkRSQKBgQD9blaWSg/2AGNhRfpX\nYq+6yKUkUD4jL7pmX1BVca6dXqILWtHl2afWeUorgv2QaK1/MJDH9Gz9Gu58hJb3\nymdeAISwPyHp8euyLIfiXSAi+ibKXkxkl1KQSweBM2oucnLsNne6Iv6QmXPpXtro\nnTMoGQDS7HVRy1on5NQLMPbUBQKBgQDwmN+um8F3CW6ZV1ZljJm7BFAgNyJ7m/5Q\nYUcOO5rFbNsHexStrx/h8jYnpdpIVlxACjh1xIyJ3lOCSAWfBWCS6KpgeO1Y484k\nEYhGjoUsKNQia8UWVt+uWnwjVSDhQjy5/pSH9xyFrUfDg8JnSlhsy0oC0C/PBjxn\nhxmADSLnNwKBgQD2A51USVMTKC9Q50BsgeU6+bmt9aNMPvHAnPf76d5q78l4IlKt\nwMs33QgOExuYirUZSgjRwknmrbUi9QckRbxwOSqVeMOwOWLm1GmYaXRf39u2CTI5\nV9gTMHJ5jnKd4gYDnaA99eiOcBhgS+9PbgKSAyuUlWwR2ciL/4uDzaVeDQKBgDym\nvRSeTRn99bSQMMZuuD5N6wkD/RxeCbEnpKrw2aZVN63eGCtkj0v9LCu4gptjseOu\n7+a4Qplqw3B/SXN5/otqPbEOKv8Shl/PT6RBv06PiFKZClkEU2T3iH27sws2EGru\nw3C3GaiVMxcVewdg1YOvh5vH8ZVlxApxIzuFlDvnAoGAN5w+gukxd5QnP/7hcLDZ\nF+vesAykJX71AuqFXB4Wh/qFY92CSm7ImexWA/L9z461+NKeJwb64Nc53z59oA10\n/3o2OcIe44kddZXQVP6KTZBd7ySVhbtOiK3/pCy+BQRsrC7d71W914DxNWadwZ+a\njtwwKjDzmPwdIXDSQarCx0U=\n-----END PRIVATE KEY-----", + passphrase: "1234", +}; + +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 the correct port", 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( + 49027, + mustCall(() => { + const address = server.address() as AddressInfo; + expect(address.address).toStrictEqual("::"); + expect(address.port).toStrictEqual(49027); + expect(address.family).toStrictEqual("IPv6"); + server.close(); + done(); + }), + ); + }); + + it("should listen on the correct port with 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( + 49026, + "0.0.0.0", + mustCall(() => { + const address = server.address() as AddressInfo; + expect(address.address).toStrictEqual("0.0.0.0"); + expect(address.port).toStrictEqual(49026); + expect(address.family).toStrictEqual("IPv4"); + 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 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", done => { + let closed = false; + const server: Server = createServer(COMMON_CERT); + server.listen().on("close", () => { + closed = true; + }); + server.close(); + expect(closed).toBe(true); + done(); + }); + + it("should call connection and drop", done => { + const { mustCall, mustNotCall } = createCallCheckCtx(done); + + let timeout: Timer; + let is_done = false; + const server = createServer(); + 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 call error", done => { + const { mustCall, mustNotCall } = createCallCheckCtx(done); + + let timeout: Timer; + const server: Server = createServer(COMMON_CERT); + + const closeAndFail = () => { + clearTimeout(timeout); + server.close(); + mustNotCall("error not called")(); + }; + + //should be faster than 100ms + timeout = setTimeout(closeAndFail, 100); + + server + .on( + "error", + mustCall(err => { + server.close(); + clearTimeout(timeout); + expect(err).toBeDefined(); + done(); + }), + ) + .listen(123456); + }); + + 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: { + 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); + }), + ); + }); +}); |