diff options
author | 2023-03-13 20:42:35 -0300 | |
---|---|---|
committer | 2023-03-13 16:42:35 -0700 | |
commit | ac9f8c0e93b6b91096a6dc8782f09a08c2e4f6c8 (patch) | |
tree | 9f710fcd00a4c2987212ab961bd57e67c0c60729 /test/js/node/net/node-net-server.test.ts | |
parent | 8d320d137393d88ada6961dc0781de9054a0c453 (diff) | |
download | bun-ac9f8c0e93b6b91096a6dc8782f09a08c2e4f6c8.tar.gz bun-ac9f8c0e93b6b91096a6dc8782f09a08c2e4f6c8.tar.zst bun-ac9f8c0e93b6b91096a6dc8782f09a08c2e4f6c8.zip |
feat(net.createServer) and adds socket.connect IPC support (#2337)
* added net.Server
* fix fmt
* fix cast and move test
* fix node-net.tests.ts but breaks server data receive
* fix end and close only emitting when data or pipe was added
* fix socket starting paused
* add echo test
* fix fmt
* fix fmt
* on close if the socket is paused, keep paused until the user resumes it to match node behavior
* resume on connect
* fix getConnections, error on close, make _Handler private and create symbols for SocketServerHandlers
* add path support (IPC)
* fix unix domains support, add connect unix tests
* fix unix socket tests
* emit listening and listening error on next tick
* fix connection flask test
* try fix connect error on macos test
* merge connection and drop tests
* added exclusive option
* fix socket.zig fmt
* fix validation for options and add test for it
* pass prettier fmt
Diffstat (limited to 'test/js/node/net/node-net-server.test.ts')
-rw-r--r-- | test/js/node/net/node-net-server.test.ts | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/test/js/node/net/node-net-server.test.ts b/test/js/node/net/node-net-server.test.ts new file mode 100644 index 000000000..bdaf289a7 --- /dev/null +++ b/test/js/node/net/node-net-server.test.ts @@ -0,0 +1,484 @@ +import { describe, expect, it } from "bun:test"; +import { createServer } from "net"; +import { createCallCheckCtx } from "node-harness"; +import { realpathSync } from "fs"; +import { tmpdir } from "os"; +import { join } from "path"; + +const socket_domain = join(realpathSync(tmpdir()), "node-net-server.sock"); + +describe("net.createServer listen", () => { + it("should throw when no port or path when using options", done => { + expect(() => createServer().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 = createServer(); + + server.on("error", mustNotCall()); + + server.listen( + 0, + mustCall(() => { + const address = server.address(); + 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 = createServer(); + + server.on("error", mustNotCall()); + + server.listen( + 0, + "127.0.0.1", + mustCall(() => { + const address = server.address(); + expect(address.address).toStrictEqual("127.0.0.1"); + //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 listen on localhost", done => { + const { mustCall, mustNotCall } = createCallCheckCtx(done); + + const server = createServer(); + + server.on("error", mustNotCall()); + + server.listen( + 0, + "::1", + mustCall(() => { + const address = server.address(); + 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 = createServer(); + + server.on("error", mustNotCall()); + + server.listen( + 0, + "::1", + mustCall(() => { + const address = server.address(); + 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 = createServer(); + + server.on("error", mustNotCall()); + + server.listen( + mustCall(() => { + const address = server.address(); + 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 = createServer(); + + server.on("error", mustNotCall()); + + server.listen( + 49027, + mustCall(() => { + const address = server.address(); + 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 = createServer(); + + server.on("error", mustNotCall()); + + server.listen( + 49026, + "127.0.0.1", + mustCall(() => { + const address = server.address(); + expect(address.address).toStrictEqual("127.0.0.1"); + 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 = createServer(); + + server.on("error", mustNotCall()); + + server.listen( + socket_domain, + mustCall(() => { + const address = server.address(); + expect(address).toStrictEqual(socket_domain); + server.close(); + }), + ); + done(); + }); +}); + +it("should receive data", done => { + const { mustCall, mustNotCall } = createCallCheckCtx(done); + let timeout; + + const onData = mustCall(data => { + clearTimeout(timeout); + server.close(); + expect(data.byteLength).toBe(5); + expect(data.toString("utf8")).toBe("Hello"); + done(); + }); + + const server = createServer(socket => { + socket.on("data", onData); + }); + + const closeAndFail = mustNotCall("no data received (timeout)", () => { + clearTimeout(timeout); + server.close(); + }); + + server.on("error", mustNotCall("no data received")); + + //should be faster than 100ms + timeout = setTimeout(() => { + closeAndFail(); + }, 100); + + server.listen( + mustCall(() => { + const address = server.address(); + Bun.connect({ + hostname: address.address, + port: address.port, + socket: { + data(socket) {}, + open(socket) { + socket.write("Hello"); + socket.end(); + }, + connectError: closeAndFail, // connection failed + }, + }).catch(closeAndFail); + }), + ); +}); + +it("should call end", done => { + const { mustCall, mustNotCall } = createCallCheckCtx(done); + let timeout; + + const onEnd = mustCall(() => { + clearTimeout(timeout); + server.close(); + done(); + }); + + const server = createServer(socket => { + socket.on("end", onEnd); + socket.end(); + }); + + const closeAndFail = mustNotCall("end not called (timeout)", () => { + clearTimeout(timeout); + server.close(); + }); + server.on("error", mustNotCall("end not called")); + + //should be faster than 100ms + timeout = setTimeout(() => { + closeAndFail(); + }, 100); + + server.listen( + mustCall(() => { + const address = server.address(); + Bun.connect({ + 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 = createServer(); + 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; + const server = createServer(); + let maxClients = 2; + server.maxConnections = maxClients - 1; + + const closeAndFail = mustNotCall("drop not called (timeout)", () => { + clearTimeout(timeout); + server.close(); + done(); + }); + + //should be faster than 100ms + timeout = setTimeout(() => { + closeAndFail(); + }, 100); + let connection_called = false; + server + .on( + "connection", + mustCall(() => { + connection_called = true; + }), + ) + .on( + "drop", + mustCall(data => { + 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(() => { + const address = server.address(); + + function spawnClient() { + Bun.connect({ + port: address.port, + hostname: address.address, + socket: { + data(socket) {}, + open(socket) { + socket.end(); + }, + }, + }); + } + for (let i = 0; i < maxClients; i++) { + spawnClient(); + spawnClient(); + } + }); +}); + +it("should call listening", done => { + const { mustCall, mustNotCall } = createCallCheckCtx(done); + + let timeout; + const server = createServer(); + let maxClients = 2; + server.maxConnections = maxClients - 1; + + const closeAndFail = mustNotCall("listening not called (timeout)", () => { + clearTimeout(timeout); + server.close(); + done(); + }); + + //should be faster than 100ms + timeout = setTimeout(() => { + closeAndFail(); + }, 100); + + server + .on( + "listening", + mustCall(() => { + server.close(); + clearTimeout(timeout); + done(); + }), + ) + .listen(); +}); + +it("should call error", done => { + const { mustCall, mustNotCall } = createCallCheckCtx(done); + + let timeout; + const server = createServer(); + let maxClients = 2; + server.maxConnections = maxClients - 1; + + const closeAndFail = mustNotCall("error not called (timeout)", () => { + clearTimeout(timeout); + server.close(); + done(); + }); + + //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; + const server = createServer(); + let maxClients = 2; + server.maxConnections = maxClients - 1; + + const closeAndFail = mustNotCall("close not called (timeout)", () => { + clearTimeout(timeout); + server.close(); + done(); + }); + + //should be faster than 100ms + timeout = setTimeout(() => { + closeAndFail(); + }, 100); + + server + .on( + "close", + mustCall(() => { + clearImmediate(timeout); + done(); + }), + ) + .listen({ port: 0, signal: controller.signal }, () => { + controller.abort(); + }); +}); + +it("should echo data", done => { + const { mustCall, mustNotCall } = createCallCheckCtx(done); + let timeout; + + const server = createServer(socket => { + socket.pipe(socket); + }); + + const closeAndFail = mustNotCall("no data received (timeout)", () => { + clearTimeout(timeout); + server.close(); + }); + + server.on("error", mustNotCall("no data received")); + + //should be faster than 100ms + timeout = setTimeout(() => { + closeAndFail(); + }, 100); + + server.listen( + mustCall(() => { + const address = server.address(); + Bun.connect({ + hostname: address.address, + port: address.port, + socket: { + data(socket, data) { + clearTimeout(timeout); + server.close(); + socket.end(); + expect(data.byteLength).toBe(5); + expect(data.toString("utf8")).toBe("Hello"); + done(); + }, + open(socket) { + socket.write("Hello"); + }, + connectError: closeAndFail, // connection failed + }, + }).catch(closeAndFail); + }), + ); +}); |