1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-19 04:16:00 -05:00
denoland-deno/tests/unit/quic_test.ts
snek ccd375802a
refactor(quic): introduce endpoint, 0rtt, cleanup (#27444)
A QUIC endpoint is a UDP socket which multiplexes QUIC sessions, which
may be initiated in either direction. This PR exposes endpoints and
moves things around as needed.

Now that endpoints can be reused between client connections, we have a
way to share tls tickets between them and allow 0rtt. This interface
currently works by conditionally returning a promise.

Also cleaned up the rust op names, fixed some lingering problems in the
data transmission, and switched to explicit error types.
2025-01-06 15:24:59 +01:00

228 lines
5.3 KiB
TypeScript

// Copyright 2018-2025 the Deno authors. MIT license.
import { assert, assertEquals } from "./test_util.ts";
const cert = Deno.readTextFileSync("tests/testdata/tls/localhost.crt");
const key = Deno.readTextFileSync("tests/testdata/tls/localhost.key");
const caCerts = [Deno.readTextFileSync("tests/testdata/tls/RootCA.pem")];
interface Pair {
server: Deno.QuicConn;
client: Deno.QuicConn;
endpoint: Deno.QuicEndpoint;
}
async function pair(opt?: Deno.QuicTransportOptions): Promise<Pair> {
const endpoint = new Deno.QuicEndpoint({ hostname: "localhost" });
const listener = endpoint.listen({
cert,
key,
alpnProtocols: ["deno-test"],
...opt,
});
assertEquals(endpoint, listener.endpoint);
const [server, client] = await Promise.all([
listener.accept(),
Deno.connectQuic({
hostname: "localhost",
port: endpoint.addr.port,
caCerts,
alpnProtocols: ["deno-test"],
...opt,
}),
]);
assertEquals(server.protocol, "deno-test");
assertEquals(client.protocol, "deno-test");
assertEquals(client.remoteAddr, endpoint.addr);
assertEquals(server.serverName, "localhost");
return { server, client, endpoint };
}
Deno.test("bidirectional stream", async () => {
const { server, client, endpoint } = await pair();
const encoded = (new TextEncoder()).encode("hi!");
{
const bi = await server.createBidirectionalStream({ sendOrder: 42 });
assertEquals(bi.writable.sendOrder, 42);
bi.writable.sendOrder = 0;
assertEquals(bi.writable.sendOrder, 0);
await bi.writable.getWriter().write(encoded);
}
{
const { value: bi } = await client.incomingBidirectionalStreams
.getReader()
.read();
const { value: data } = await bi!.readable.getReader().read();
assertEquals(data, encoded);
}
client.close();
endpoint.close();
});
Deno.test("unidirectional stream", async () => {
const { server, client, endpoint } = await pair();
const encoded = (new TextEncoder()).encode("hi!");
{
const uni = await server.createUnidirectionalStream({ sendOrder: 42 });
assertEquals(uni.sendOrder, 42);
uni.sendOrder = 0;
assertEquals(uni.sendOrder, 0);
await uni.getWriter().write(encoded);
}
{
const { value: uni } = await client.incomingUnidirectionalStreams
.getReader()
.read();
const { value: data } = await uni!.getReader().read();
assertEquals(data, encoded);
}
endpoint.close();
client.close();
});
Deno.test("datagrams", async () => {
const { server, client, endpoint } = await pair();
const encoded = (new TextEncoder()).encode("hi!");
await server.sendDatagram(encoded);
const data = await client.readDatagram();
assertEquals(data, encoded);
endpoint.close();
client.close();
});
Deno.test("closing", async () => {
const { server, client } = await pair();
server.close({ closeCode: 42, reason: "hi!" });
assertEquals(await client.closed, { closeCode: 42, reason: "hi!" });
});
Deno.test("max concurrent streams", async () => {
const { server, client, endpoint } = await pair({
maxConcurrentBidirectionalStreams: 1,
maxConcurrentUnidirectionalStreams: 1,
});
{
await server.createBidirectionalStream();
await server.createBidirectionalStream()
.then(() => {
throw new Error("expected failure");
}, () => {
// success!
});
}
{
await server.createUnidirectionalStream();
await server.createUnidirectionalStream()
.then(() => {
throw new Error("expected failure");
}, () => {
// success!
});
}
endpoint.close();
client.close();
});
Deno.test("incoming", async () => {
const endpoint = new Deno.QuicEndpoint({ hostname: "localhost" });
const listener = endpoint.listen({
cert,
key,
alpnProtocols: ["deno-test"],
});
const connect = () =>
Deno.connectQuic({
hostname: "localhost",
port: endpoint.addr.port,
caCerts,
alpnProtocols: ["deno-test"],
});
const c1p = connect();
const i1 = await listener.incoming();
const server = await i1.accept();
const client = await c1p;
assertEquals(server.protocol, "deno-test");
assertEquals(client.protocol, "deno-test");
assertEquals(client.remoteAddr, endpoint.addr);
endpoint.close();
client.close();
});
Deno.test("0rtt", async () => {
const sEndpoint = new Deno.QuicEndpoint({ hostname: "localhost" });
const listener = sEndpoint.listen({
cert,
key,
alpnProtocols: ["deno-test"],
});
(async () => {
while (true) {
let incoming;
try {
incoming = await listener.incoming();
} catch (e) {
if (e instanceof Deno.errors.BadResource) {
break;
}
throw e;
}
const conn = incoming.accept({ zeroRtt: true });
conn.handshake.then(() => {
conn.close();
});
}
})();
const endpoint = new Deno.QuicEndpoint();
const c1 = await Deno.connectQuic({
hostname: "localhost",
port: sEndpoint.addr.port,
caCerts,
alpnProtocols: ["deno-test"],
endpoint,
});
await c1.closed;
const c2 = Deno.connectQuic({
hostname: "localhost",
port: sEndpoint.addr.port,
caCerts,
alpnProtocols: ["deno-test"],
zeroRtt: true,
endpoint,
});
assert(!(c2 instanceof Promise), "0rtt should be accepted");
await c2.closed;
sEndpoint.close();
endpoint.close();
});