2023-01-02 16:00:42 -05:00
|
|
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2021-08-09 18:28:17 -04:00
|
|
|
|
|
|
|
import {
|
2021-12-16 06:57:26 -05:00
|
|
|
assert,
|
2021-08-09 18:28:17 -04:00
|
|
|
assertEquals,
|
2022-01-05 11:41:44 -05:00
|
|
|
assertNotEquals,
|
2021-11-28 06:25:49 -05:00
|
|
|
assertRejects,
|
2021-08-09 18:28:17 -04:00
|
|
|
assertThrows,
|
2021-12-16 06:57:26 -05:00
|
|
|
unreachable,
|
2023-11-21 23:13:56 -05:00
|
|
|
} from "../../../../test_util/std/testing/asserts.ts";
|
2021-08-09 18:28:17 -04:00
|
|
|
|
|
|
|
Deno.test("fragment", () => {
|
|
|
|
assertThrows(() => new WebSocketStream("ws://localhost:4242/#"));
|
|
|
|
assertThrows(() => new WebSocketStream("ws://localhost:4242/#foo"));
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("duplicate protocols", () => {
|
|
|
|
assertThrows(() =>
|
|
|
|
new WebSocketStream("ws://localhost:4242", {
|
|
|
|
protocols: ["foo", "foo"],
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("connect & close custom valid code", async () => {
|
|
|
|
const ws = new WebSocketStream("ws://localhost:4242");
|
2023-10-11 01:31:05 -04:00
|
|
|
await ws.opened;
|
2021-08-09 18:28:17 -04:00
|
|
|
ws.close({ code: 1000 });
|
|
|
|
await ws.closed;
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("connect & close custom invalid reason", async () => {
|
|
|
|
const ws = new WebSocketStream("ws://localhost:4242");
|
2023-10-11 01:31:05 -04:00
|
|
|
await ws.opened;
|
2021-08-09 18:28:17 -04:00
|
|
|
assertThrows(() => ws.close({ code: 1000, reason: "".padEnd(124, "o") }));
|
|
|
|
ws.close();
|
|
|
|
await ws.closed;
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("echo string", async () => {
|
|
|
|
const ws = new WebSocketStream("ws://localhost:4242");
|
2023-10-11 01:31:05 -04:00
|
|
|
const { readable, writable } = await ws.opened;
|
2021-08-09 18:28:17 -04:00
|
|
|
await writable.getWriter().write("foo");
|
|
|
|
const res = await readable.getReader().read();
|
|
|
|
assertEquals(res.value, "foo");
|
|
|
|
ws.close();
|
|
|
|
await ws.closed;
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("echo string tls", async () => {
|
|
|
|
const ws = new WebSocketStream("wss://localhost:4243");
|
2023-10-11 01:31:05 -04:00
|
|
|
const { readable, writable } = await ws.opened;
|
2021-08-09 18:28:17 -04:00
|
|
|
await writable.getWriter().write("foo");
|
|
|
|
const res = await readable.getReader().read();
|
|
|
|
assertEquals(res.value, "foo");
|
|
|
|
ws.close();
|
|
|
|
await ws.closed;
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("websocket error", async () => {
|
|
|
|
const ws = new WebSocketStream("wss://localhost:4242");
|
|
|
|
await Promise.all([
|
2021-11-28 06:25:49 -05:00
|
|
|
assertRejects(
|
2023-10-11 01:31:05 -04:00
|
|
|
() => ws.opened,
|
2021-08-09 18:28:17 -04:00
|
|
|
Deno.errors.UnexpectedEof,
|
|
|
|
"tls handshake eof",
|
|
|
|
),
|
2021-11-28 06:25:49 -05:00
|
|
|
assertRejects(
|
2021-08-09 18:28:17 -04:00
|
|
|
() => ws.closed,
|
|
|
|
Deno.errors.UnexpectedEof,
|
|
|
|
"tls handshake eof",
|
|
|
|
),
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("echo uint8array", async () => {
|
|
|
|
const ws = new WebSocketStream("ws://localhost:4242");
|
2023-10-11 01:31:05 -04:00
|
|
|
const { readable, writable } = await ws.opened;
|
2021-08-09 18:28:17 -04:00
|
|
|
const uint = new Uint8Array([102, 111, 111]);
|
|
|
|
await writable.getWriter().write(uint);
|
|
|
|
const res = await readable.getReader().read();
|
|
|
|
assertEquals(res.value, uint);
|
|
|
|
ws.close();
|
|
|
|
await ws.closed;
|
|
|
|
});
|
2021-11-28 06:25:49 -05:00
|
|
|
|
|
|
|
Deno.test("aborting immediately throws an AbortError", async () => {
|
|
|
|
const controller = new AbortController();
|
|
|
|
const wss = new WebSocketStream("ws://localhost:4242", {
|
|
|
|
signal: controller.signal,
|
|
|
|
});
|
|
|
|
controller.abort();
|
|
|
|
await assertRejects(
|
2023-10-11 01:31:05 -04:00
|
|
|
() => wss.opened,
|
2021-12-16 06:57:26 -05:00
|
|
|
(error: Error) => {
|
|
|
|
assert(error instanceof DOMException);
|
|
|
|
assertEquals(error.name, "AbortError");
|
|
|
|
},
|
|
|
|
);
|
|
|
|
await assertRejects(
|
|
|
|
() => wss.closed,
|
|
|
|
(error: Error) => {
|
|
|
|
assert(error instanceof DOMException);
|
|
|
|
assertEquals(error.name, "AbortError");
|
|
|
|
},
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("aborting immediately with a reason throws that reason", async () => {
|
|
|
|
const controller = new AbortController();
|
|
|
|
const wss = new WebSocketStream("ws://localhost:4242", {
|
|
|
|
signal: controller.signal,
|
|
|
|
});
|
|
|
|
const abortReason = new Error();
|
|
|
|
controller.abort(abortReason);
|
|
|
|
await assertRejects(
|
2023-10-11 01:31:05 -04:00
|
|
|
() => wss.opened,
|
2021-12-16 06:57:26 -05:00
|
|
|
(error: Error) => assertEquals(error, abortReason),
|
|
|
|
);
|
|
|
|
await assertRejects(
|
|
|
|
() => wss.closed,
|
|
|
|
(error: Error) => assertEquals(error, abortReason),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("aborting immediately with a primitive as reason throws that primitive", async () => {
|
|
|
|
const controller = new AbortController();
|
|
|
|
const wss = new WebSocketStream("ws://localhost:4242", {
|
|
|
|
signal: controller.signal,
|
|
|
|
});
|
|
|
|
controller.abort("Some string");
|
2023-10-11 01:31:05 -04:00
|
|
|
await wss.opened.then(
|
2021-12-16 06:57:26 -05:00
|
|
|
() => unreachable(),
|
|
|
|
(e) => assertEquals(e, "Some string"),
|
|
|
|
);
|
|
|
|
await wss.closed.then(
|
|
|
|
() => unreachable(),
|
|
|
|
(e) => assertEquals(e, "Some string"),
|
2021-11-28 06:25:49 -05:00
|
|
|
);
|
|
|
|
});
|
2022-01-05 11:41:44 -05:00
|
|
|
|
|
|
|
Deno.test("headers", async () => {
|
2022-02-16 10:51:32 -05:00
|
|
|
const listener = Deno.listen({ port: 4512 });
|
2022-01-05 11:41:44 -05:00
|
|
|
const promise = (async () => {
|
2022-02-16 10:51:32 -05:00
|
|
|
const conn = await listener.accept();
|
|
|
|
const httpConn = Deno.serveHttp(conn);
|
2022-01-05 11:41:44 -05:00
|
|
|
const { request, respondWith } = (await httpConn.nextRequest())!;
|
|
|
|
assertEquals(request.headers.get("x-some-header"), "foo");
|
2022-02-16 10:51:32 -05:00
|
|
|
const { response, socket } = Deno.upgradeWebSocket(request);
|
2022-01-05 11:41:44 -05:00
|
|
|
socket.onopen = () => socket.close();
|
2022-02-16 10:51:32 -05:00
|
|
|
const p = new Promise<void>((resolve) => {
|
|
|
|
socket.onopen = () => socket.close();
|
|
|
|
socket.onclose = () => resolve();
|
|
|
|
});
|
2022-01-05 11:41:44 -05:00
|
|
|
await respondWith(response);
|
2022-02-16 10:51:32 -05:00
|
|
|
await p;
|
2022-01-05 11:41:44 -05:00
|
|
|
})();
|
|
|
|
|
2022-02-16 10:51:32 -05:00
|
|
|
const ws = new WebSocketStream("ws://localhost:4512", {
|
2022-01-05 11:41:44 -05:00
|
|
|
headers: [["x-some-header", "foo"]],
|
|
|
|
});
|
2023-10-11 01:31:05 -04:00
|
|
|
await ws.opened;
|
2022-01-05 11:41:44 -05:00
|
|
|
await promise;
|
|
|
|
await ws.closed;
|
|
|
|
listener.close();
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("forbidden headers", async () => {
|
|
|
|
const forbiddenHeaders = [
|
|
|
|
"sec-websocket-accept",
|
|
|
|
"sec-websocket-extensions",
|
|
|
|
"sec-websocket-key",
|
|
|
|
"sec-websocket-protocol",
|
|
|
|
"sec-websocket-version",
|
|
|
|
"upgrade",
|
|
|
|
"connection",
|
|
|
|
];
|
|
|
|
|
2022-02-16 10:51:32 -05:00
|
|
|
const listener = Deno.listen({ port: 4512 });
|
2022-01-05 11:41:44 -05:00
|
|
|
const promise = (async () => {
|
2022-02-16 10:51:32 -05:00
|
|
|
const conn = await listener.accept();
|
|
|
|
const httpConn = Deno.serveHttp(conn);
|
2022-01-05 11:41:44 -05:00
|
|
|
const { request, respondWith } = (await httpConn.nextRequest())!;
|
2022-02-16 10:51:32 -05:00
|
|
|
for (const [key] of request.headers) {
|
|
|
|
assertNotEquals(key, "foo");
|
2022-01-05 11:41:44 -05:00
|
|
|
}
|
2022-02-16 10:51:32 -05:00
|
|
|
const { response, socket } = Deno.upgradeWebSocket(request);
|
|
|
|
const p = new Promise<void>((resolve) => {
|
|
|
|
socket.onopen = () => socket.close();
|
|
|
|
socket.onclose = () => resolve();
|
|
|
|
});
|
2022-01-05 11:41:44 -05:00
|
|
|
await respondWith(response);
|
2022-02-16 10:51:32 -05:00
|
|
|
await p;
|
2022-01-05 11:41:44 -05:00
|
|
|
})();
|
|
|
|
|
2022-02-16 10:51:32 -05:00
|
|
|
const ws = new WebSocketStream("ws://localhost:4512", {
|
2022-01-05 11:41:44 -05:00
|
|
|
headers: forbiddenHeaders.map((header) => [header, "foo"]),
|
|
|
|
});
|
2023-10-11 01:31:05 -04:00
|
|
|
await ws.opened;
|
2022-01-05 11:41:44 -05:00
|
|
|
await promise;
|
|
|
|
await ws.closed;
|
|
|
|
listener.close();
|
|
|
|
});
|
2022-08-29 21:43:17 -04:00
|
|
|
|
|
|
|
Deno.test("sync close with empty stream", async () => {
|
|
|
|
const listener = Deno.listen({ port: 4512 });
|
|
|
|
const promise = (async () => {
|
|
|
|
const conn = await listener.accept();
|
|
|
|
const httpConn = Deno.serveHttp(conn);
|
|
|
|
const { request, respondWith } = (await httpConn.nextRequest())!;
|
|
|
|
const { response, socket } = Deno.upgradeWebSocket(request);
|
|
|
|
const p = new Promise<void>((resolve) => {
|
|
|
|
socket.onopen = () => {
|
|
|
|
socket.send("first message");
|
|
|
|
socket.send("second message");
|
|
|
|
};
|
|
|
|
socket.onclose = () => resolve();
|
|
|
|
});
|
|
|
|
await respondWith(response);
|
|
|
|
await p;
|
|
|
|
})();
|
|
|
|
|
|
|
|
const ws = new WebSocketStream("ws://localhost:4512");
|
2023-10-11 01:31:05 -04:00
|
|
|
const { readable } = await ws.opened;
|
2022-08-29 21:43:17 -04:00
|
|
|
const reader = readable.getReader();
|
|
|
|
const firstMessage = await reader.read();
|
|
|
|
assertEquals(firstMessage.value, "first message");
|
|
|
|
const secondMessage = await reader.read();
|
|
|
|
assertEquals(secondMessage.value, "second message");
|
|
|
|
ws.close({ code: 1000 });
|
|
|
|
await ws.closed;
|
|
|
|
await promise;
|
|
|
|
listener.close();
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("sync close with unread messages in stream", async () => {
|
|
|
|
const listener = Deno.listen({ port: 4512 });
|
|
|
|
const promise = (async () => {
|
|
|
|
const conn = await listener.accept();
|
|
|
|
const httpConn = Deno.serveHttp(conn);
|
|
|
|
const { request, respondWith } = (await httpConn.nextRequest())!;
|
|
|
|
const { response, socket } = Deno.upgradeWebSocket(request);
|
|
|
|
const p = new Promise<void>((resolve) => {
|
|
|
|
socket.onopen = () => {
|
|
|
|
socket.send("first message");
|
|
|
|
socket.send("second message");
|
|
|
|
socket.send("third message");
|
|
|
|
socket.send("fourth message");
|
|
|
|
};
|
|
|
|
socket.onclose = () => resolve();
|
|
|
|
});
|
|
|
|
await respondWith(response);
|
|
|
|
await p;
|
|
|
|
})();
|
|
|
|
|
|
|
|
const ws = new WebSocketStream("ws://localhost:4512");
|
2023-10-11 01:31:05 -04:00
|
|
|
const { readable } = await ws.opened;
|
2022-08-29 21:43:17 -04:00
|
|
|
const reader = readable.getReader();
|
|
|
|
const firstMessage = await reader.read();
|
|
|
|
assertEquals(firstMessage.value, "first message");
|
|
|
|
const secondMessage = await reader.read();
|
|
|
|
assertEquals(secondMessage.value, "second message");
|
|
|
|
ws.close({ code: 1000 });
|
|
|
|
await ws.closed;
|
|
|
|
await promise;
|
|
|
|
listener.close();
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("async close with empty stream", async () => {
|
|
|
|
const listener = Deno.listen({ port: 4512 });
|
|
|
|
const promise = (async () => {
|
|
|
|
const conn = await listener.accept();
|
|
|
|
const httpConn = Deno.serveHttp(conn);
|
|
|
|
const { request, respondWith } = (await httpConn.nextRequest())!;
|
|
|
|
const { response, socket } = Deno.upgradeWebSocket(request);
|
|
|
|
const p = new Promise<void>((resolve) => {
|
|
|
|
socket.onopen = () => {
|
|
|
|
socket.send("first message");
|
|
|
|
socket.send("second message");
|
|
|
|
};
|
|
|
|
socket.onclose = () => resolve();
|
|
|
|
});
|
|
|
|
await respondWith(response);
|
|
|
|
await p;
|
|
|
|
})();
|
|
|
|
|
|
|
|
const ws = new WebSocketStream("ws://localhost:4512");
|
2023-10-11 01:31:05 -04:00
|
|
|
const { readable } = await ws.opened;
|
2022-08-29 21:43:17 -04:00
|
|
|
const reader = readable.getReader();
|
|
|
|
const firstMessage = await reader.read();
|
|
|
|
assertEquals(firstMessage.value, "first message");
|
|
|
|
const secondMessage = await reader.read();
|
|
|
|
assertEquals(secondMessage.value, "second message");
|
|
|
|
setTimeout(() => {
|
|
|
|
ws.close({ code: 1000 });
|
|
|
|
}, 0);
|
|
|
|
await ws.closed;
|
|
|
|
await promise;
|
|
|
|
listener.close();
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("async close with unread messages in stream", async () => {
|
|
|
|
const listener = Deno.listen({ port: 4512 });
|
|
|
|
const promise = (async () => {
|
|
|
|
const conn = await listener.accept();
|
|
|
|
const httpConn = Deno.serveHttp(conn);
|
|
|
|
const { request, respondWith } = (await httpConn.nextRequest())!;
|
|
|
|
const { response, socket } = Deno.upgradeWebSocket(request);
|
|
|
|
const p = new Promise<void>((resolve) => {
|
|
|
|
socket.onopen = () => {
|
|
|
|
socket.send("first message");
|
|
|
|
socket.send("second message");
|
|
|
|
socket.send("third message");
|
|
|
|
socket.send("fourth message");
|
|
|
|
};
|
|
|
|
socket.onclose = () => resolve();
|
|
|
|
});
|
|
|
|
await respondWith(response);
|
|
|
|
await p;
|
|
|
|
})();
|
|
|
|
|
|
|
|
const ws = new WebSocketStream("ws://localhost:4512");
|
2023-10-11 01:31:05 -04:00
|
|
|
const { readable } = await ws.opened;
|
2022-08-29 21:43:17 -04:00
|
|
|
const reader = readable.getReader();
|
|
|
|
const firstMessage = await reader.read();
|
|
|
|
assertEquals(firstMessage.value, "first message");
|
|
|
|
const secondMessage = await reader.read();
|
|
|
|
assertEquals(secondMessage.value, "second message");
|
|
|
|
setTimeout(() => {
|
|
|
|
ws.close({ code: 1000 });
|
|
|
|
}, 0);
|
|
|
|
await ws.closed;
|
|
|
|
await promise;
|
|
|
|
listener.close();
|
|
|
|
});
|