mirror of
https://github.com/denoland/deno.git
synced 2025-01-03 04:48:52 -05:00
65b647909d
Implements a QUIC interface, loosely based on the WebTransport API (a future change could add the WebTransport API, built on top of this one). [quinn](https://docs.rs/quinn/latest/quinn/) is used for the underlying QUIC implementation, for a few reasons: - A cloneable "handle" api which fits quite nicely into deno resources. - Good collaboration with the rust ecosystem, especially rustls. - I like it. <!-- Before submitting a PR, please read https://deno.com/manual/contributing 1. Give the PR a descriptive title. Examples of good title: - fix(std/http): Fix race condition in server - docs(console): Update docstrings - feat(doc): Handle nested reexports Examples of bad title: - fix #7123 - update docs - fix bugs 2. Ensure there is a related issue and it is referenced in the PR text. 3. Ensure there are tests that cover the changes. 4. Ensure `cargo test` passes. 5. Ensure `./tools/format.js` passes without changing files. 6. Ensure `./tools/lint.js` passes. 7. Open as a draft PR if your work is still in progress. The CI won't run all steps, but you can add '[ci]' to a commit message to force it to. 8. If you would like to run the benchmarks on the CI, add the 'ci-bench' label. -->
172 lines
4.4 KiB
TypeScript
172 lines
4.4 KiB
TypeScript
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
|
|
import { 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")];
|
|
|
|
async function pair(opt?: Deno.QuicTransportOptions): Promise<
|
|
[Deno.QuicConn, Deno.QuicConn, Deno.QuicListener]
|
|
> {
|
|
const listener = await Deno.listenQuic({
|
|
hostname: "localhost",
|
|
port: 0,
|
|
cert,
|
|
key,
|
|
alpnProtocols: ["deno-test"],
|
|
...opt,
|
|
});
|
|
|
|
const [server, client] = await Promise.all([
|
|
listener.accept(),
|
|
Deno.connectQuic({
|
|
hostname: "localhost",
|
|
port: listener.addr.port,
|
|
caCerts,
|
|
alpnProtocols: ["deno-test"],
|
|
...opt,
|
|
}),
|
|
]);
|
|
|
|
assertEquals(server.protocol, "deno-test");
|
|
assertEquals(client.protocol, "deno-test");
|
|
assertEquals(client.remoteAddr, listener.addr);
|
|
|
|
return [server, client, listener];
|
|
}
|
|
|
|
Deno.test("bidirectional stream", async () => {
|
|
const [server, client, listener] = 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);
|
|
}
|
|
|
|
listener.close({ closeCode: 0, reason: "" });
|
|
client.close({ closeCode: 0, reason: "" });
|
|
});
|
|
|
|
Deno.test("unidirectional stream", async () => {
|
|
const [server, client, listener] = 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);
|
|
}
|
|
|
|
listener.close({ closeCode: 0, reason: "" });
|
|
client.close({ closeCode: 0, reason: "" });
|
|
});
|
|
|
|
Deno.test("datagrams", async () => {
|
|
const [server, client, listener] = await pair();
|
|
|
|
const encoded = (new TextEncoder()).encode("hi!");
|
|
|
|
await server.sendDatagram(encoded);
|
|
|
|
const data = await client.readDatagram();
|
|
assertEquals(data, encoded);
|
|
|
|
listener.close({ closeCode: 0, reason: "" });
|
|
client.close({ closeCode: 0, reason: "" });
|
|
});
|
|
|
|
Deno.test("closing", async () => {
|
|
const [server, client, listener] = await pair();
|
|
|
|
server.close({ closeCode: 42, reason: "hi!" });
|
|
|
|
assertEquals(await client.closed, { closeCode: 42, reason: "hi!" });
|
|
|
|
listener.close({ closeCode: 0, reason: "" });
|
|
});
|
|
|
|
Deno.test("max concurrent streams", async () => {
|
|
const [server, client, listener] = 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!
|
|
});
|
|
}
|
|
|
|
listener.close({ closeCode: 0, reason: "" });
|
|
server.close({ closeCode: 0, reason: "" });
|
|
client.close({ closeCode: 0, reason: "" });
|
|
});
|
|
|
|
Deno.test("incoming", async () => {
|
|
const listener = await Deno.listenQuic({
|
|
hostname: "localhost",
|
|
port: 0,
|
|
cert,
|
|
key,
|
|
alpnProtocols: ["deno-test"],
|
|
});
|
|
|
|
const connect = () =>
|
|
Deno.connectQuic({
|
|
hostname: "localhost",
|
|
port: listener.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, listener.addr);
|
|
|
|
listener.close({ closeCode: 0, reason: "" });
|
|
client.close({ closeCode: 0, reason: "" });
|
|
});
|