Files
bun.sh/test/js/node/net/node-net-server.test.ts

572 lines
14 KiB
TypeScript

// @known-failing-on-windows: 1 failing
import { createServer, Server, AddressInfo, Socket } from "net";
import { realpathSync } from "fs";
import { tmpdir } from "os";
import { join } from "path";
import { createTest } from "node-harness";
const { describe, expect, it, createCallCheckCtx } = createTest(import.meta.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: Server = createServer();
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();
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();
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 provide listening property", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer();
expect(server.listening).toBeFalse();
let timeout: Timer;
const closeAndFail = () => {
clearTimeout(timeout);
server.close();
mustNotCall()();
};
server.on("error", closeAndFail).on(
"listening",
mustCall(() => {
expect(server.listening).toBeTrue();
clearTimeout(timeout);
server.close();
expect(server.listening).toBeFalse();
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();
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();
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();
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();
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 bind IPv4 0.0.0.0 when listen on 0.0.0.0, issue#7355", done => {
const { mustCall, mustNotCall } = createCallCheckCtx(done);
const server: Server = createServer();
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(async () => {
const address = server.address() as AddressInfo;
expect(address.address).toStrictEqual("0.0.0.0");
expect(address.family).toStrictEqual("IPv4");
let err: Error | null = null;
try {
await Bun.connect({
hostname: "0.0.0.0",
port: address.port,
socket: {
data(socket) {},
},
});
} catch (e) {
err = e as Error;
}
expect(err).toBeNull();
try {
await Bun.connect({
hostname: "::",
port: address.port,
socket: {
data(socket) {},
},
});
} catch (e) {
err = e as Error;
}
expect(err).not.toBeNull();
expect(err!.message).toBe("Failed to connect");
expect(err!.name).toBe("ECONNREFUSED");
server.close();
done();
}),
);
});
});
describe("net.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((socket: Socket) => {
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({
hostname: address.address,
port: address.port,
socket: {
data(socket) {},
open(socket) {
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((socket: Socket) => {
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({
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();
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({
port: address?.port,
hostname: address?.address,
socket: {
data(socket) {},
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();
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();
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 { mustNotCall } = createCallCheckCtx(done);
let timeout: Timer;
let client: any = null;
const server: Server = createServer((socket: Socket) => {
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(async () => {
const address = server.address() as AddressInfo;
client = await Bun.connect({
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();
},
open(socket) {
socket.write("Hello");
},
connectError: closeAndFail, // connection failed
},
}).catch(closeAndFail);
});
});
});