import { ServerWebSocket, TCPSocket, Socket as _BunSocket, TCPSocketListener } from "bun"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "bun:test"; import { connect, isIP, isIPv4, isIPv6, Socket, createConnection } from "net"; import { realpathSync, mkdtempSync } from "fs"; import { tmpdir } from "os"; import { join } from "path"; const socket_domain = mkdtempSync(join(realpathSync(tmpdir()), "node-net")); it("should support net.isIP()", () => { expect(isIP("::1")).toBe(6); expect(isIP("foobar")).toBe(0); expect(isIP("127.0.0.1")).toBe(4); expect(isIP("127.0.0.1/24")).toBe(0); expect(isIP("127.000.000.001")).toBe(0); }); it("should support net.isIPv4()", () => { expect(isIPv4("::1")).toBe(false); expect(isIPv4("foobar")).toBe(false); expect(isIPv4("127.0.0.1")).toBe(true); expect(isIPv4("127.0.0.1/24")).toBe(false); expect(isIPv4("127.000.000.001")).toBe(false); }); it("should support net.isIPv6()", () => { expect(isIPv6("::1")).toBe(true); expect(isIPv6("foobar")).toBe(false); expect(isIPv6("127.0.0.1")).toBe(false); expect(isIPv6("127.0.0.1/24")).toBe(false); expect(isIPv6("127.000.000.001")).toBe(false); }); describe("net.Socket read", () => { var port = 12345; var unix_servers = 0; for (let [message, label] of [ // ["Hello World!".repeat(1024), "long message"], ["Hello!", "short message"], ]) { describe(label, () => { function runWithServer(cb: (..._: any[]) => void, unix_domain_path?: any) { return (done: (_: any) => void) => { function drain(socket: _BunSocket<{ message: string }>) { const message = socket.data.message; const written = socket.write(message); if (written < message.length) { socket.data.message = message.slice(written); } else { socket.end(); } } var server = unix_domain_path ? Bun.listen({ unix: join(unix_domain_path, `${unix_servers++}.sock`), socket: { open(socket) { socket.data.message = message; drain(socket); }, drain, error(socket, err) { done(err); }, }, data: { message: "", }, }) : Bun.listen({ hostname: "localhost", port: 0, socket: { open(socket) { socket.data.message = message; drain(socket); }, drain, error(socket, err) { done(err); }, }, data: { message: "", }, }); function onDone(err: any) { server.stop(); done(err); } try { cb(server, drain, onDone); } catch (e) { onDone(e); } }; } it( "should work with .connect(port)", runWithServer((server, drain, done) => { var data = ""; const socket = new Socket() .connect(server.port) .on("connect", () => { expect(socket).toBeDefined(); expect(socket.connecting).toBe(false); }) .setEncoding("utf8") .on("data", chunk => { data += chunk; }) .on("end", () => { try { expect(data).toBe(message); done(); } catch (e) { server.stop(); done(e); } }) .on("error", done); }), ); it( "should work with .connect(port, listener)", runWithServer((server, drain, done) => { var data = ""; const socket = new Socket() .connect(server.port, () => { expect(socket).toBeDefined(); expect(socket.connecting).toBe(false); }) .setEncoding("utf8") .on("data", chunk => { data += chunk; }) .on("end", () => { try { expect(data).toBe(message); done(); } catch (e) { server.stop(); done(e); } }) .on("error", done); }), ); it( "should work with .connect(port, host, listener)", runWithServer((server, drain, done) => { var data = ""; const socket = new Socket() .connect(server.port, "localhost", () => { expect(socket).toBeDefined(); expect(socket.connecting).toBe(false); }) .setEncoding("utf8") .on("data", chunk => { data += chunk; }) .on("end", () => { try { expect(data).toBe(message); done(); } catch (e) { done(e); } }) .on("error", done); }), ); it( "should work with .createConnection(path)", runWithServer((server, drain, done) => { var data = ""; const socket = createConnection(server.unix) .on("connect", () => { expect(socket).toBeDefined(); expect(socket.connecting).toBe(false); }) .setEncoding("utf8") .on("data", chunk => { data += chunk; }) .on("end", () => { try { expect(data).toBe(message); done(); } catch (e) { server.stop(); done(e); } }) .on("error", done); }, socket_domain), ); it( "should work with .connect(path)", runWithServer((server, drain, done) => { var data = ""; const socket = new Socket() .connect(server.unix) .on("connect", () => { expect(socket).toBeDefined(); expect(socket.connecting).toBe(false); }) .setEncoding("utf8") .on("data", chunk => { data += chunk; }) .on("end", () => { try { expect(data).toBe(message); done(); } catch (e) { server.stop(); done(e); } }) .on("error", done); }, socket_domain), ); it( "should work with .connect(path, listener)", runWithServer((server, drain, done) => { var data = ""; const socket = new Socket() .connect(server.unix, () => { expect(socket).toBeDefined(); expect(socket.connecting).toBe(false); }) .setEncoding("utf8") .on("data", chunk => { data += chunk; }) .on("end", () => { try { expect(data).toBe(message); done(); } catch (e) { done(e); } }) .on("error", done); }, socket_domain), ); }); } }); describe("net.Socket write", () => { const message = "Hello World!".repeat(1024); let port = 53213; function runWithServer(cb: (..._: any[]) => void) { return (done: (_?: any) => void) => { let server: TCPSocketListener; function close(socket: _BunSocket) { expect(Buffer.concat(socket.data).toString("utf8")).toBe(message); done(); } var leaky; server = Bun.listen({ hostname: "0.0.0.0", port: 0, socket: { close, data(socket, buffer) { leaky = socket; if (!Buffer.isBuffer(buffer)) { done(new Error("buffer is not a Buffer")); } socket.data.push(buffer); }, end: close, error(socket, err) { leaky = socket; done(err); }, open(socket) { leaky = socket; socket.data = []; }, }, data: [] as Buffer[], }); function onDone(err: any) { server.stop(); done(err); } try { cb(server, onDone); } catch (e) { onDone(e); } }; } it( "should work with .end(data)", runWithServer((server, done) => { const socket = new Socket() .connect(server.port) .on("ready", () => { expect(socket).toBeDefined(); expect(socket.connecting).toBe(false); }) .on("error", done) .end(message); }), ); it( "should work with .write(data).end()", runWithServer((server, done) => { const socket = new Socket() .connect(server.port, () => { expect(socket).toBeDefined(); expect(socket.connecting).toBe(false); }) .on("error", done); socket.write(message); socket.end(); }), ); it( "should work with multiple .write()s", runWithServer((server, done) => { const socket = new Socket() .connect(server.port, server.hostname, () => { expect(socket).toBeDefined(); expect(socket.connecting).toBe(false); }) .on("error", done); const size = 10; for (let i = 0; i < message.length; i += size) { socket.write(message.slice(i, i + size)); } socket.end(); }), ); }); it("should handle connection error", done => { let errored = false; // @ts-ignore const socket = connect(55555, () => { done(new Error("Should not have connected")); }); socket.on("error", error => { if (errored) { return done(new Error("Should not have errored twice")); } errored = true; expect(error).toBeDefined(); expect(error.message).toBe("Failed to connect"); expect((error as any).code).toBe("ECONNREFUSED"); }); socket.on("connect", () => { done(new Error("Should not have connected")); }); socket.on("close", () => { expect(errored).toBe(true); done(); }); }); it("should handle connection error (unix)", done => { let errored = false; // @ts-ignore const socket = connect("loser", () => { done(new Error("Should not have connected")); }); socket.on("error", error => { if (errored) { return done(new Error("Should not have errored twice")); } errored = true; expect(error).toBeDefined(); expect(error.message).toBe("Failed to connect"); expect((error as any).code).toBe("ENOENT"); }); socket.on("connect", () => { done(new Error("Should not have connected")); }); socket.on("close", () => { expect(errored).toBe(true); done(); }); });