mirror of
https://github.com/denoland/deno.git
synced 2024-12-27 17:49:08 -05:00
430b63c2c4
This commit improves async op sanitizer speed by only delaying metrics collection if there are pending ops. This results in a speedup of around 30% for small CPU bound unit tests. It performs this check and possible delay on every collection now, fixing an issue with parent test leaks into steps.
1249 lines
34 KiB
TypeScript
1249 lines
34 KiB
TypeScript
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
import {
|
|
assert,
|
|
assertEquals,
|
|
assertNotEquals,
|
|
assertRejects,
|
|
assertThrows,
|
|
deferred,
|
|
delay,
|
|
execCode,
|
|
execCode2,
|
|
} from "./test_util.ts";
|
|
import { join } from "../../../test_util/std/path/mod.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();
|
|
},
|
|
);
|
|
|
|
function tmpUnixSocketPath(): string {
|
|
const folder = Deno.makeTempDirSync();
|
|
return join(folder, "socket");
|
|
}
|
|
|
|
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 closeDeferred = deferred();
|
|
listener.accept().then(async (conn) => {
|
|
await conn.write(new Uint8Array([1, 2, 3]));
|
|
await closeDeferred;
|
|
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]));
|
|
});
|
|
closeDeferred.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();
|
|
});
|