deno/cli/tests/unit/net_test.ts
2024-01-01 19:58:21 +00:00

1275 lines
35 KiB
TypeScript

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import {
assert,
assertEquals,
assertNotEquals,
assertRejects,
assertThrows,
delay,
execCode,
execCode2,
tmpUnixSocketPath,
} from "./test_util.ts";
// Since these tests may run in parallel, ensure this port is unique to this file
const listenPort = 4503;
const listenPort2 = 4504;
let isCI: boolean;
try {
isCI = Deno.env.get("CI") !== undefined;
} catch {
isCI = true;
}
Deno.test({ permissions: { net: true } }, function netTcpListenClose() {
const listener = Deno.listen({ hostname: "127.0.0.1", port: listenPort });
assert(listener.addr.transport === "tcp");
assertEquals(listener.addr.hostname, "127.0.0.1");
assertEquals(listener.addr.port, listenPort);
assertNotEquals(listener.rid, 0);
listener.close();
});
Deno.test(
{
permissions: { net: true },
},
function netUdpListenClose() {
const socket = Deno.listenDatagram({
hostname: "127.0.0.1",
port: listenPort,
transport: "udp",
});
assert(socket.addr.transport === "udp");
assertEquals(socket.addr.hostname, "127.0.0.1");
assertEquals(socket.addr.port, listenPort);
socket.close();
},
);
Deno.test(
{
ignore: Deno.build.os === "windows",
permissions: { read: true, write: true },
},
function netUnixListenClose() {
const filePath = tmpUnixSocketPath();
const socket = Deno.listen({
path: filePath,
transport: "unix",
});
assert(socket.addr.transport === "unix");
assertEquals(socket.addr.path, filePath);
socket.close();
},
);
Deno.test(
{
ignore: Deno.build.os === "windows",
permissions: { read: true, write: true },
},
function netUnixPacketListenClose() {
const filePath = tmpUnixSocketPath();
const socket = Deno.listenDatagram({
path: filePath,
transport: "unixpacket",
});
assert(socket.addr.transport === "unixpacket");
assertEquals(socket.addr.path, filePath);
socket.close();
},
);
Deno.test(
{
ignore: Deno.build.os === "windows",
permissions: { read: true, write: false },
},
function netUnixListenWritePermission() {
assertThrows(() => {
const filePath = tmpUnixSocketPath();
const socket = Deno.listen({
path: filePath,
transport: "unix",
});
assert(socket.addr.transport === "unix");
assertEquals(socket.addr.path, filePath);
socket.close();
}, Deno.errors.PermissionDenied);
},
);
Deno.test(
{
ignore: Deno.build.os === "windows",
permissions: { read: true, write: false },
},
function netUnixPacketListenWritePermission() {
assertThrows(() => {
const filePath = tmpUnixSocketPath();
const socket = Deno.listenDatagram({
path: filePath,
transport: "unixpacket",
});
assert(socket.addr.transport === "unixpacket");
assertEquals(socket.addr.path, filePath);
socket.close();
}, Deno.errors.PermissionDenied);
},
);
Deno.test(
{
permissions: { net: true },
},
async function netTcpCloseWhileAccept() {
const listener = Deno.listen({ port: listenPort });
const p = listener.accept();
listener.close();
// TODO(piscisaureus): the error type should be `Interrupted` here, which
// gets thrown, but then ext/net catches it and rethrows `BadResource`.
await assertRejects(
() => p,
Deno.errors.BadResource,
"Listener has been closed",
);
},
);
Deno.test(
{
ignore: Deno.build.os === "windows",
permissions: { read: true, write: true },
},
async function netUnixCloseWhileAccept() {
const filePath = tmpUnixSocketPath();
const listener = Deno.listen({
path: filePath,
transport: "unix",
});
const p = listener.accept();
listener.close();
await assertRejects(
() => p,
Deno.errors.BadResource,
"Listener has been closed",
);
},
);
Deno.test(
{ permissions: { net: true } },
async function netTcpConcurrentAccept() {
const listener = Deno.listen({ port: 4510 });
let acceptErrCount = 0;
const checkErr = (e: Error) => {
if (e.message === "Listener has been closed") {
assertEquals(acceptErrCount, 1);
} else if (e.message === "Another accept task is ongoing") {
acceptErrCount++;
} else {
throw new Error("Unexpected error message");
}
};
const p = listener.accept().catch(checkErr);
const p1 = listener.accept().catch(checkErr);
await Promise.race([p, p1]);
listener.close();
await Promise.all([p, p1]);
assertEquals(acceptErrCount, 1);
},
);
Deno.test(
{
ignore: Deno.build.os === "windows",
permissions: { read: true, write: true },
},
async function netUnixConcurrentAccept() {
const filePath = tmpUnixSocketPath();
const listener = Deno.listen({ transport: "unix", path: filePath });
let acceptErrCount = 0;
const checkErr = (e: Error) => {
if (e.message === "Listener has been closed") {
assertEquals(acceptErrCount, 1);
} else if (e instanceof Deno.errors.Busy) { // "Listener already in use"
acceptErrCount++;
} else {
throw e;
}
};
const p = listener.accept().catch(checkErr);
const p1 = listener.accept().catch(checkErr);
await Promise.race([p, p1]);
listener.close();
await Promise.all([p, p1]);
assertEquals(acceptErrCount, 1);
},
);
Deno.test({ permissions: { net: true } }, async function netTcpDialListen() {
const listener = Deno.listen({ port: listenPort });
listener.accept().then(
async (conn) => {
assert(conn.remoteAddr != null);
assert(conn.localAddr.transport === "tcp");
assertEquals(conn.localAddr.hostname, "127.0.0.1");
assertEquals(conn.localAddr.port, listenPort);
await conn.write(new Uint8Array([1, 2, 3]));
conn.close();
},
);
const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
assert(conn.remoteAddr.transport === "tcp");
assertEquals(conn.remoteAddr.hostname, "127.0.0.1");
assertEquals(conn.remoteAddr.port, listenPort);
assert(conn.localAddr != null);
const buf = new Uint8Array(1024);
const readResult = await conn.read(buf);
assertEquals(3, readResult);
assertEquals(1, buf[0]);
assertEquals(2, buf[1]);
assertEquals(3, buf[2]);
assert(conn.rid > 0);
assert(readResult !== null);
const readResult2 = await conn.read(buf);
assertEquals(readResult2, null);
listener.close();
conn.close();
});
Deno.test({ permissions: { net: true } }, async function netTcpSetNoDelay() {
const listener = Deno.listen({ port: listenPort });
listener.accept().then(
async (conn) => {
assert(conn.remoteAddr != null);
assert(conn.localAddr.transport === "tcp");
assertEquals(conn.localAddr.hostname, "127.0.0.1");
assertEquals(conn.localAddr.port, listenPort);
await conn.write(new Uint8Array([1, 2, 3]));
conn.close();
},
);
const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
conn.setNoDelay(true);
assert(conn.remoteAddr.transport === "tcp");
assertEquals(conn.remoteAddr.hostname, "127.0.0.1");
assertEquals(conn.remoteAddr.port, listenPort);
assert(conn.localAddr != null);
const buf = new Uint8Array(1024);
const readResult = await conn.read(buf);
assertEquals(3, readResult);
assertEquals(1, buf[0]);
assertEquals(2, buf[1]);
assertEquals(3, buf[2]);
assert(conn.rid > 0);
assert(readResult !== null);
const readResult2 = await conn.read(buf);
assertEquals(readResult2, null);
listener.close();
conn.close();
});
Deno.test({ permissions: { net: true } }, async function netTcpSetKeepAlive() {
const listener = Deno.listen({ port: listenPort });
listener.accept().then(
async (conn) => {
assert(conn.remoteAddr != null);
assert(conn.localAddr.transport === "tcp");
assertEquals(conn.localAddr.hostname, "127.0.0.1");
assertEquals(conn.localAddr.port, listenPort);
await conn.write(new Uint8Array([1, 2, 3]));
conn.close();
},
);
const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
conn.setKeepAlive(true);
assert(conn.remoteAddr.transport === "tcp");
assertEquals(conn.remoteAddr.hostname, "127.0.0.1");
assertEquals(conn.remoteAddr.port, listenPort);
assert(conn.localAddr != null);
const buf = new Uint8Array(1024);
const readResult = await conn.read(buf);
assertEquals(3, readResult);
assertEquals(1, buf[0]);
assertEquals(2, buf[1]);
assertEquals(3, buf[2]);
assert(conn.rid > 0);
assert(readResult !== null);
const readResult2 = await conn.read(buf);
assertEquals(readResult2, null);
listener.close();
conn.close();
});
Deno.test(
{
ignore: Deno.build.os === "windows",
permissions: { read: true, write: true },
},
async function netUnixDialListen() {
const filePath = tmpUnixSocketPath();
const listener = Deno.listen({ path: filePath, transport: "unix" });
listener.accept().then(
async (conn) => {
assert(conn.remoteAddr != null);
assert(conn.localAddr.transport === "unix");
assertEquals(conn.localAddr.path, filePath);
await conn.write(new Uint8Array([1, 2, 3]));
conn.close();
},
);
const conn = await Deno.connect({ path: filePath, transport: "unix" });
assert(conn.remoteAddr.transport === "unix");
assertEquals(conn.remoteAddr.path, filePath);
assert(conn.remoteAddr != null);
const buf = new Uint8Array(1024);
const readResult = await conn.read(buf);
assertEquals(3, readResult);
assertEquals(1, buf[0]);
assertEquals(2, buf[1]);
assertEquals(3, buf[2]);
assert(conn.rid > 0);
assert(readResult !== null);
const readResult2 = await conn.read(buf);
assertEquals(readResult2, null);
listener.close();
conn.close();
},
);
Deno.test(
{ permissions: { net: true } },
async function netUdpSendReceive() {
const alice = Deno.listenDatagram({ port: listenPort, transport: "udp" });
assert(alice.addr.transport === "udp");
assertEquals(alice.addr.port, listenPort);
assertEquals(alice.addr.hostname, "127.0.0.1");
const bob = Deno.listenDatagram({ port: listenPort2, transport: "udp" });
assert(bob.addr.transport === "udp");
assertEquals(bob.addr.port, listenPort2);
assertEquals(bob.addr.hostname, "127.0.0.1");
const sent = new Uint8Array([1, 2, 3]);
const byteLength = await alice.send(sent, bob.addr);
assertEquals(byteLength, 3);
const [recvd, remote] = await bob.receive();
assert(remote.transport === "udp");
assertEquals(remote.port, listenPort);
assertEquals(recvd.length, 3);
assertEquals(1, recvd[0]);
assertEquals(2, recvd[1]);
assertEquals(3, recvd[2]);
alice.close();
bob.close();
},
);
Deno.test(
{ permissions: { net: true }, ignore: true },
async function netUdpSendReceiveBroadcast() {
// Must bind sender to an address that can send to the broadcast address on MacOS.
// Macos will give us error 49 when sending the broadcast packet if we omit hostname here.
const alice = Deno.listenDatagram({
port: listenPort,
transport: "udp",
hostname: "0.0.0.0",
});
const bob = Deno.listenDatagram({
port: listenPort,
transport: "udp",
hostname: "0.0.0.0",
});
assert(bob.addr.transport === "udp");
assertEquals(bob.addr.port, listenPort);
assertEquals(bob.addr.hostname, "0.0.0.0");
const broadcastAddr = { ...bob.addr, hostname: "255.255.255.255" };
const sent = new Uint8Array([1, 2, 3]);
const byteLength = await alice.send(sent, broadcastAddr);
assertEquals(byteLength, 3);
const [recvd, remote] = await bob.receive();
assert(remote.transport === "udp");
assertEquals(remote.port, listenPort);
assertEquals(recvd.length, 3);
assertEquals(1, recvd[0]);
assertEquals(2, recvd[1]);
assertEquals(3, recvd[2]);
alice.close();
bob.close();
},
);
Deno.test(
{ permissions: { net: true }, ignore: true },
async function netUdpMulticastV4() {
const listener = Deno.listenDatagram({
hostname: "0.0.0.0",
port: 5353,
transport: "udp",
reuseAddress: true,
});
const membership = await listener.joinMulticastV4(
"224.0.0.251",
"127.0.0.1",
);
membership.setLoopback(true);
membership.setLoopback(false);
membership.setTTL(50);
membership.leave();
listener.close();
},
);
Deno.test(
{ permissions: { net: true }, ignore: true },
async function netUdpMulticastV6() {
const listener = Deno.listenDatagram({
hostname: "::",
port: 5353,
transport: "udp",
reuseAddress: true,
});
const membership = await listener.joinMulticastV6(
"ff02::fb",
1,
);
membership.setLoopback(true);
membership.setLoopback(false);
membership.leave();
listener.close();
},
);
Deno.test(
{ permissions: { net: true }, ignore: true },
async function netUdpSendReceiveMulticastv4() {
const alice = Deno.listenDatagram({
hostname: "0.0.0.0",
port: 5353,
transport: "udp",
reuseAddress: true,
loopback: true,
});
const bob = Deno.listenDatagram({
hostname: "0.0.0.0",
port: 5353,
transport: "udp",
reuseAddress: true,
});
const aliceMembership = await alice.joinMulticastV4(
"224.0.0.1",
"0.0.0.0",
);
const bobMembership = await bob.joinMulticastV4("224.0.0.1", "0.0.0.0");
const sent = new Uint8Array([1, 2, 3]);
await alice.send(sent, {
hostname: "224.0.0.1",
port: 5353,
transport: "udp",
});
const [recvd, remote] = await bob.receive();
assert(remote.transport === "udp");
assertEquals(remote.port, 5353);
assertEquals(recvd.length, 3);
assertEquals(1, recvd[0]);
assertEquals(2, recvd[1]);
assertEquals(3, recvd[2]);
aliceMembership.leave();
bobMembership.leave();
alice.close();
bob.close();
},
);
Deno.test(
{ permissions: { net: true }, ignore: true },
async function netUdpMulticastLoopbackOption() {
// Must bind sender to an address that can send to the broadcast address on MacOS.
// Macos will give us error 49 when sending the broadcast packet if we omit hostname here.
const listener = Deno.listenDatagram({
port: 5353,
transport: "udp",
hostname: "0.0.0.0",
loopback: true,
reuseAddress: true,
});
const membership = await listener.joinMulticastV4(
"224.0.0.1",
"0.0.0.0",
);
// await membership.setLoopback(true);
const sent = new Uint8Array([1, 2, 3]);
const byteLength = await listener.send(sent, {
hostname: "224.0.0.1",
port: 5353,
transport: "udp",
});
assertEquals(byteLength, 3);
const [recvd, remote] = await listener.receive();
assert(remote.transport === "udp");
assertEquals(remote.port, 5353);
assertEquals(recvd.length, 3);
assertEquals(1, recvd[0]);
assertEquals(2, recvd[1]);
assertEquals(3, recvd[2]);
membership.leave();
listener.close();
},
);
Deno.test(
{ permissions: { net: true } },
async function netUdpConcurrentSendReceive() {
const socket = Deno.listenDatagram({ port: listenPort, transport: "udp" });
assert(socket.addr.transport === "udp");
assertEquals(socket.addr.port, listenPort);
assertEquals(socket.addr.hostname, "127.0.0.1");
const recvPromise = socket.receive();
const sendBuf = new Uint8Array([1, 2, 3]);
const sendLen = await socket.send(sendBuf, socket.addr);
assertEquals(sendLen, 3);
const [recvBuf, _recvAddr] = await recvPromise;
assertEquals(recvBuf.length, 3);
assertEquals(1, recvBuf[0]);
assertEquals(2, recvBuf[1]);
assertEquals(3, recvBuf[2]);
socket.close();
},
);
Deno.test(
{ permissions: { net: true } },
async function netUdpBorrowMutError() {
const socket = Deno.listenDatagram({
port: listenPort,
transport: "udp",
});
// Panic happened on second send: BorrowMutError
const a = socket.send(new Uint8Array(), socket.addr);
const b = socket.send(new Uint8Array(), socket.addr);
await Promise.all([a, b]);
socket.close();
},
);
Deno.test(
{
ignore: Deno.build.os === "windows",
permissions: { read: true, write: true },
},
async function netUnixPacketSendReceive() {
const aliceFilePath = tmpUnixSocketPath();
const alice = Deno.listenDatagram({
path: aliceFilePath,
transport: "unixpacket",
});
assert(alice.addr.transport === "unixpacket");
assertEquals(alice.addr.path, aliceFilePath);
const bobFilePath = tmpUnixSocketPath();
const bob = Deno.listenDatagram({
path: bobFilePath,
transport: "unixpacket",
});
assert(bob.addr.transport === "unixpacket");
assertEquals(bob.addr.path, bobFilePath);
const sent = new Uint8Array([1, 2, 3]);
const byteLength = await alice.send(sent, bob.addr);
assertEquals(byteLength, 3);
const [recvd, remote] = await bob.receive();
assert(remote.transport === "unixpacket");
assertEquals(remote.path, aliceFilePath);
assertEquals(recvd.length, 3);
assertEquals(1, recvd[0]);
assertEquals(2, recvd[1]);
assertEquals(3, recvd[2]);
alice.close();
bob.close();
},
);
// TODO(lucacasonato): support concurrent reads and writes on unixpacket sockets
Deno.test(
{ ignore: true, permissions: { read: true, write: true } },
async function netUnixPacketConcurrentSendReceive() {
const filePath = tmpUnixSocketPath();
const socket = Deno.listenDatagram({
path: filePath,
transport: "unixpacket",
});
assert(socket.addr.transport === "unixpacket");
assertEquals(socket.addr.path, filePath);
const recvPromise = socket.receive();
const sendBuf = new Uint8Array([1, 2, 3]);
const sendLen = await socket.send(sendBuf, socket.addr);
assertEquals(sendLen, 3);
const [recvBuf, _recvAddr] = await recvPromise;
assertEquals(recvBuf.length, 3);
assertEquals(1, recvBuf[0]);
assertEquals(2, recvBuf[1]);
assertEquals(3, recvBuf[2]);
socket.close();
},
);
Deno.test(
{ permissions: { net: true } },
async function netTcpListenIteratorBreakClosesResource() {
async function iterate(listener: Deno.Listener) {
let i = 0;
for await (const conn of listener) {
conn.close();
i++;
if (i > 1) {
break;
}
}
}
const addr = { hostname: "127.0.0.1", port: 8888 };
const listener = Deno.listen(addr);
const iteratePromise = iterate(listener);
await delay(100);
const conn1 = await Deno.connect(addr);
conn1.close();
const conn2 = await Deno.connect(addr);
conn2.close();
await iteratePromise;
},
);
Deno.test(
{ permissions: { net: true } },
async function netTcpListenCloseWhileIterating() {
const listener = Deno.listen({ port: 8001 });
const nextWhileClosing = listener[Symbol.asyncIterator]().next();
listener.close();
assertEquals(await nextWhileClosing, { value: undefined, done: true });
const nextAfterClosing = listener[Symbol.asyncIterator]().next();
assertEquals(await nextAfterClosing, { value: undefined, done: true });
},
);
Deno.test(
{ permissions: { net: true } },
async function netUdpListenCloseWhileIterating() {
const socket = Deno.listenDatagram({ port: 8000, transport: "udp" });
const nextWhileClosing = socket[Symbol.asyncIterator]().next();
socket.close();
assertEquals(await nextWhileClosing, { value: undefined, done: true });
const nextAfterClosing = socket[Symbol.asyncIterator]().next();
assertEquals(await nextAfterClosing, { value: undefined, done: true });
},
);
Deno.test(
{
ignore: Deno.build.os === "windows",
permissions: { read: true, write: true },
},
async function netUnixListenCloseWhileIterating() {
const filePath = tmpUnixSocketPath();
const socket = Deno.listen({ path: filePath, transport: "unix" });
const nextWhileClosing = socket[Symbol.asyncIterator]().next();
socket.close();
assertEquals(await nextWhileClosing, { value: undefined, done: true });
const nextAfterClosing = socket[Symbol.asyncIterator]().next();
assertEquals(await nextAfterClosing, { value: undefined, done: true });
},
);
Deno.test(
{
ignore: Deno.build.os === "windows",
permissions: { read: true, write: true },
},
async function netUnixPacketListenCloseWhileIterating() {
const filePath = tmpUnixSocketPath();
const socket = Deno.listenDatagram({
path: filePath,
transport: "unixpacket",
});
const nextWhileClosing = socket[Symbol.asyncIterator]().next();
socket.close();
assertEquals(await nextWhileClosing, { value: undefined, done: true });
const nextAfterClosing = socket[Symbol.asyncIterator]().next();
assertEquals(await nextAfterClosing, { value: undefined, done: true });
},
);
Deno.test(
{ permissions: { net: true } },
async function netListenAsyncIterator() {
const addr = { hostname: "127.0.0.1", port: listenPort };
const listener = Deno.listen(addr);
const runAsyncIterator = async () => {
for await (const conn of listener) {
await conn.write(new Uint8Array([1, 2, 3]));
conn.close();
}
};
runAsyncIterator();
const conn = await Deno.connect(addr);
const buf = new Uint8Array(1024);
const readResult = await conn.read(buf);
assertEquals(3, readResult);
assertEquals(1, buf[0]);
assertEquals(2, buf[1]);
assertEquals(3, buf[2]);
assert(conn.rid > 0);
assert(readResult !== null);
const readResult2 = await conn.read(buf);
assertEquals(readResult2, null);
listener.close();
conn.close();
},
);
Deno.test(
{
permissions: { net: true },
},
async function netCloseWriteSuccess() {
const addr = { hostname: "127.0.0.1", port: listenPort };
const listener = Deno.listen(addr);
const { promise: closePromise, resolve } = Promise.withResolvers<void>();
listener.accept().then(async (conn) => {
await conn.write(new Uint8Array([1, 2, 3]));
await closePromise;
conn.close();
});
const conn = await Deno.connect(addr);
conn.closeWrite(); // closing write
const buf = new Uint8Array(1024);
// Check read not impacted
const readResult = await conn.read(buf);
assertEquals(3, readResult);
assertEquals(1, buf[0]);
assertEquals(2, buf[1]);
assertEquals(3, buf[2]);
// Verify that the write end of the socket is closed.
// TODO(piscisaureus): assert that thrown error is of a specific type.
await assertRejects(async () => {
await conn.write(new Uint8Array([1, 2, 3]));
});
resolve();
listener.close();
conn.close();
},
);
Deno.test(
{
// https://github.com/denoland/deno/issues/11580
ignore: Deno.build.os === "darwin" && isCI,
permissions: { net: true },
},
async function netHangsOnClose() {
let acceptedConn: Deno.Conn;
async function iteratorReq(listener: Deno.Listener) {
const p = new Uint8Array(10);
const conn = await listener.accept();
acceptedConn = conn;
try {
while (true) {
const nread = await conn.read(p);
if (nread === null) {
break;
}
await conn.write(new Uint8Array([1, 2, 3]));
}
} catch (err) {
assert(err);
assert(err instanceof Deno.errors.Interrupted);
}
}
const addr = { hostname: "127.0.0.1", port: listenPort };
const listener = Deno.listen(addr);
const listenerPromise = iteratorReq(listener);
const connectionPromise = (async () => {
const conn = await Deno.connect(addr);
await conn.write(new Uint8Array([1, 2, 3, 4]));
const buf = new Uint8Array(10);
await conn.read(buf);
conn!.close();
acceptedConn!.close();
listener.close();
})();
await Promise.all([
listenerPromise,
connectionPromise,
]);
},
);
Deno.test(
{
permissions: { net: true },
},
function netExplicitUndefinedHostname() {
const listener = Deno.listen({ hostname: undefined, port: 8080 });
assertEquals((listener.addr as Deno.NetAddr).hostname, "0.0.0.0");
listener.close();
},
);
Deno.test(
{
ignore: Deno.build.os !== "linux",
permissions: { read: true, write: true },
},
function netUnixAbstractPathShouldNotPanic() {
const listener = Deno.listen({
path: "\0aaa",
transport: "unix",
});
assert("not panic");
listener.close();
},
);
Deno.test({ permissions: { net: true } }, async function whatwgStreams() {
const server = (async () => {
const listener = Deno.listen({ hostname: "127.0.0.1", port: listenPort });
const conn = await listener.accept();
await conn.readable.pipeTo(conn.writable);
listener.close();
})();
const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
const reader = conn.readable.getReader();
const writer = conn.writable.getWriter();
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const data = encoder.encode("Hello World");
await writer.write(data);
const { value, done } = await reader.read();
assert(!done);
assertEquals(decoder.decode(value), "Hello World");
await reader.cancel();
await server;
});
Deno.test(
{ permissions: { read: true } },
async function readableStreamTextEncoderPipe() {
const filename = "cli/tests/testdata/assets/hello.txt";
const file = await Deno.open(filename);
const readable = file.readable.pipeThrough(new TextDecoderStream());
const chunks = [];
for await (const chunk of readable) {
chunks.push(chunk);
}
assertEquals(chunks.length, 1);
assertEquals(chunks[0].length, 12);
},
);
Deno.test(
{ permissions: { read: true, write: true } },
async function writableStream() {
const path = await Deno.makeTempFile();
const file = await Deno.open(path, { write: true });
assert(file.writable instanceof WritableStream);
const readable = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode("hello "));
controller.enqueue(new TextEncoder().encode("world!"));
controller.close();
},
});
await readable.pipeTo(file.writable);
const res = await Deno.readTextFile(path);
assertEquals(res, "hello world!");
},
);
Deno.test(
{ permissions: { read: true, run: true } },
async function netListenUnref() {
const [statusCode, _output] = await execCode(`
async function main() {
const listener = Deno.listen({ port: ${listenPort} });
listener.unref();
await listener.accept(); // This doesn't block the program from exiting
}
main();
`);
assertEquals(statusCode, 0);
},
);
Deno.test(
{ permissions: { read: true, run: true } },
async function netListenUnref2() {
const [statusCode, _output] = await execCode(`
async function main() {
const listener = Deno.listen({ port: ${listenPort} });
await listener.accept();
listener.unref();
await listener.accept(); // The program exits here
throw new Error(); // The program doesn't reach here
}
main();
const conn = await Deno.connect({ port: ${listenPort} });
conn.close();
`);
assertEquals(statusCode, 0);
},
);
Deno.test(
{ permissions: { read: true, run: true, net: true } },
async function netListenUnrefAndRef() {
const p = execCode2(`
async function main() {
const listener = Deno.listen({ port: ${listenPort} });
listener.unref();
listener.ref(); // This restores 'ref' state of listener
console.log("started");
await listener.accept();
console.log("accepted")
}
main();
`);
await p.waitStdoutText("started");
const conn = await Deno.connect({ port: listenPort });
conn.close();
const [statusCode, output] = await p.finished();
assertEquals(statusCode, 0);
assertEquals(output.trim(), "started\naccepted");
},
);
Deno.test(
{ permissions: { net: true } },
async function netListenUnrefConcurrentAccept() {
const timer = setTimeout(() => {}, 1000);
const listener = Deno.listen({ port: listenPort });
listener.accept().catch(() => {});
listener.unref();
// Unref'd listener still causes Busy error
// on concurrent accept calls.
await assertRejects(async () => {
await listener.accept(); // The program exits here
}, Deno.errors.Busy);
listener.close();
clearTimeout(timer);
},
);
Deno.test({
ignore: Deno.build.os === "windows",
permissions: { read: true, write: true },
}, function netUnixListenAddrAlreadyInUse() {
const filePath = tmpUnixSocketPath();
const listener = Deno.listen({ path: filePath, transport: "unix" });
assertThrows(
() => {
Deno.listen({ path: filePath, transport: "unix" });
},
Deno.errors.AddrInUse,
);
listener.close();
});
Deno.test(
{ permissions: { net: true, read: true, run: true } },
async function netConnUnref() {
const listener = Deno.listen({ port: listenPort });
const intervalId = setInterval(() => {}); // This keeps event loop alive.
const program = execCode(`
async function main() {
const conn = await Deno.connect({ port: ${listenPort} });
conn.unref();
await conn.read(new Uint8Array(10)); // The program exits here
throw new Error(); // The program doesn't reach here
}
main();
`);
const conn = await listener.accept();
const [statusCode, _output] = await program;
conn.close();
listener.close();
clearInterval(intervalId);
assertEquals(statusCode, 0);
},
);
Deno.test(
{ permissions: { net: true, read: true, run: true } },
async function netConnUnrefReadable() {
const listener = Deno.listen({ port: listenPort });
const intervalId = setInterval(() => {}); // This keeps event loop alive.
const program = execCode(`
async function main() {
const conn = await Deno.connect({ port: ${listenPort} });
conn.unref();
const reader = conn.readable.getReader();
await reader.read(); // The program exits here
throw new Error(); // The program doesn't reach here
}
main();
`);
const conn = await listener.accept();
const [statusCode, _output] = await program;
conn.close();
listener.close();
clearInterval(intervalId);
assertEquals(statusCode, 0);
},
);
Deno.test({ permissions: { net: true } }, async function netTcpReuseAddr() {
const listener1 = Deno.listen({
hostname: "127.0.0.1",
port: listenPort,
});
listener1.accept().then(
(conn) => {
conn.close();
},
);
const conn1 = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
const buf1 = new Uint8Array(1024);
await conn1.read(buf1);
listener1.close();
conn1.close();
const listener2 = Deno.listen({
hostname: "127.0.0.1",
port: listenPort,
});
listener2.accept().then(
(conn) => {
conn.close();
},
);
const conn2 = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
const buf2 = new Uint8Array(1024);
await conn2.read(buf2);
listener2.close();
conn2.close();
});
Deno.test(
{ permissions: { net: true } },
async function netUdpReuseAddr() {
const sender = Deno.listenDatagram({
port: 4002,
transport: "udp",
});
const listener1 = Deno.listenDatagram({
port: 4000,
transport: "udp",
reuseAddress: true,
});
const listener2 = Deno.listenDatagram({
port: 4000,
transport: "udp",
reuseAddress: true,
});
const sent = new Uint8Array([1, 2, 3]);
await sender.send(sent, listener1.addr);
await Promise.any([listener1.receive(), listener2.receive()]).then(
([recvd, remote]) => {
assert(remote.transport === "udp");
assertEquals(recvd.length, 3);
assertEquals(1, recvd[0]);
assertEquals(2, recvd[1]);
assertEquals(3, recvd[2]);
},
);
sender.close();
listener1.close();
listener2.close();
},
);
Deno.test(
{ permissions: { net: true } },
function netUdpNoReuseAddr() {
let listener1;
try {
listener1 = Deno.listenDatagram({
port: 4001,
transport: "udp",
reuseAddress: false,
});
} catch (err) {
assert(err);
assert(err instanceof Deno.errors.AddrInUse); // AddrInUse from previous test
}
assertThrows(() => {
Deno.listenDatagram({
port: 4001,
transport: "udp",
reuseAddress: false,
});
}, Deno.errors.AddrInUse);
if (typeof listener1 !== "undefined") {
listener1.close();
}
},
);
Deno.test({
ignore: Deno.build.os !== "linux",
permissions: { net: true },
}, async function netTcpListenReusePort() {
const port = 4003;
const listener1 = Deno.listen({ port, reusePort: true });
const listener2 = Deno.listen({ port, reusePort: true });
let p1;
let p2;
let listener1Recv = false;
let listener2Recv = false;
while (!listener1Recv || !listener2Recv) {
if (!p1) {
p1 = listener1.accept().then((conn) => {
conn.close();
listener1Recv = true;
p1 = undefined;
}).catch(() => {});
}
if (!p2) {
p2 = listener2.accept().then((conn) => {
conn.close();
listener2Recv = true;
p2 = undefined;
}).catch(() => {});
}
const conn = await Deno.connect({ port });
conn.close();
await Promise.race([p1, p2]);
}
listener1.close();
listener2.close();
});
Deno.test({
ignore: Deno.build.os === "linux",
permissions: { net: true },
}, function netTcpListenReusePortDoesNothing() {
const listener1 = Deno.listen({ port: 4003, reusePort: true });
assertThrows(() => {
Deno.listen({ port: 4003, reusePort: true });
}, Deno.errors.AddrInUse);
listener1.close();
});
Deno.test({
permissions: { net: true },
}, function netTcpListenDoesNotThrowOnStringPort() {
// @ts-ignore String port is not allowed by typing, but it shouldn't throw
// for backwards compatibility.
const listener = Deno.listen({ hostname: "localhost", port: "0" });
listener.close();
});
Deno.test(
{ permissions: { net: true } },
async function listenerExplicitResourceManagement() {
let done: Promise<Deno.errors.BadResource>;
{
using listener = Deno.listen({ port: listenPort });
done = assertRejects(
() => listener.accept(),
Deno.errors.BadResource,
);
}
await done;
},
);
Deno.test(
{ permissions: { net: true } },
async function listenerExplicitResourceManagementManualClose() {
using listener = Deno.listen({ port: listenPort });
listener.close();
await assertRejects( // definitely closed
() => listener.accept(),
Deno.errors.BadResource,
);
// calling [Symbol.dispose] after manual close is a no-op
},
);