mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 11:59:00 +00:00
201 lines
6.2 KiB
TypeScript
201 lines
6.2 KiB
TypeScript
import { serve } from "bun";
|
|
import { expect, test } from "bun:test";
|
|
|
|
// Test compressed continuation frames
|
|
test("WebSocket client handles compressed continuation frames correctly", async () => {
|
|
using server = serve({
|
|
port: 0,
|
|
fetch(req, server) {
|
|
if (server.upgrade(req)) {
|
|
return;
|
|
}
|
|
return new Response("Not found", { status: 404 });
|
|
},
|
|
websocket: {
|
|
perMessageDeflate: true,
|
|
open(ws) {
|
|
// Send a message that should be compressed
|
|
const largeMessage = "A".repeat(100000); // 100KB of A's
|
|
const result = ws.send(largeMessage, true);
|
|
if (result <= 0) {
|
|
throw new Error(`Failed to send large message, result: ${result}`);
|
|
}
|
|
},
|
|
message(ws, message) {
|
|
// Echo back
|
|
const result = ws.send(message, true);
|
|
if (result <= 0) {
|
|
throw new Error(`Failed to echo message, result: ${result}`);
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
const client = new WebSocket(`ws://localhost:${server.port}`);
|
|
|
|
const { promise: openPromise, resolve: resolveOpen, reject: rejectOpen } = Promise.withResolvers<void>();
|
|
client.onopen = () => resolveOpen();
|
|
client.onerror = error => rejectOpen(error);
|
|
client.onclose = event => {
|
|
if (!event.wasClean) {
|
|
rejectOpen(new Error(`WebSocket closed: code=${event.code}, reason=${event.reason}`));
|
|
}
|
|
};
|
|
|
|
await openPromise;
|
|
expect(client.extensions).toContain("permessage-deflate");
|
|
|
|
const { promise: messagePromise, resolve: resolveMessage } = Promise.withResolvers<string>();
|
|
client.onmessage = event => resolveMessage(event.data);
|
|
|
|
const receivedMessage = await messagePromise;
|
|
expect(receivedMessage).toBe("A".repeat(100000));
|
|
|
|
client.close();
|
|
server.stop();
|
|
});
|
|
|
|
// Test small message compression threshold
|
|
test("WebSocket client doesn't compress small messages", async () => {
|
|
let serverReceivedCompressed = false;
|
|
|
|
using server = serve({
|
|
port: 0,
|
|
fetch(req, server) {
|
|
if (server.upgrade(req)) {
|
|
return;
|
|
}
|
|
return new Response("Not found", { status: 404 });
|
|
},
|
|
websocket: {
|
|
perMessageDeflate: true,
|
|
open(ws) {
|
|
// Track if messages are compressed by checking frame headers
|
|
},
|
|
message(ws, message) {
|
|
// Small messages should not be compressed (< 860 bytes)
|
|
const result = ws.send("OK", true);
|
|
if (result <= 0) {
|
|
throw new Error(`Failed to send OK response, result: ${result}`);
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
const client = new WebSocket(`ws://localhost:${server.port}`);
|
|
|
|
const { promise: openPromise, resolve: resolveOpen, reject: rejectOpen } = Promise.withResolvers<void>();
|
|
client.onopen = () => resolveOpen();
|
|
client.onerror = error => rejectOpen(error);
|
|
|
|
await openPromise;
|
|
|
|
const { promise: messagePromise, resolve: resolveMessage } = Promise.withResolvers<string>();
|
|
client.onmessage = event => resolveMessage(event.data);
|
|
|
|
// Send a small message (should not be compressed)
|
|
client.send("Hello");
|
|
|
|
const receivedMessage = await messagePromise;
|
|
expect(receivedMessage).toBe("OK");
|
|
|
|
client.close();
|
|
server.stop();
|
|
});
|
|
|
|
// Test message size limits
|
|
test("WebSocket client rejects messages exceeding size limit", async () => {
|
|
// This test would require a custom server that sends extremely large compressed data
|
|
// For now, we'll test that normal large messages work
|
|
using server = serve({
|
|
port: 0,
|
|
fetch(req, server) {
|
|
if (server.upgrade(req)) {
|
|
return;
|
|
}
|
|
return new Response("Not found", { status: 404 });
|
|
},
|
|
websocket: {
|
|
perMessageDeflate: true,
|
|
open(ws) {
|
|
// Send a 1MB message (under the 100MB limit) with more realistic data
|
|
// Using varied content to avoid triggering compression bomb detection
|
|
const size = 1 * 1024 * 1024;
|
|
const pattern = "The quick brown fox jumps over the lazy dog. ";
|
|
const buffer = Buffer.alloc(size);
|
|
buffer.fill(pattern);
|
|
const result = ws.send(buffer, true);
|
|
if (result <= 0) {
|
|
throw new Error(`Failed to send large buffer, result: ${result}`);
|
|
}
|
|
},
|
|
message(ws, message) {},
|
|
},
|
|
});
|
|
|
|
const client = new WebSocket(`ws://localhost:${server.port}`);
|
|
|
|
const { promise: openPromise, resolve: resolveOpen, reject: rejectOpen } = Promise.withResolvers<void>();
|
|
client.onopen = () => resolveOpen();
|
|
client.onerror = error => rejectOpen(error);
|
|
|
|
const { promise: messagePromise, resolve: resolveMessage } = Promise.withResolvers<string>();
|
|
client.onmessage = event => resolveMessage(event.data);
|
|
|
|
await openPromise;
|
|
const receivedMessage = await messagePromise;
|
|
expect(receivedMessage.length).toBe(1 * 1024 * 1024);
|
|
|
|
client.close();
|
|
server.stop();
|
|
});
|
|
|
|
// Test compression error handling
|
|
test("WebSocket client handles compression errors gracefully", async () => {
|
|
using server = serve({
|
|
port: 0,
|
|
fetch(req, server) {
|
|
if (server.upgrade(req)) {
|
|
return;
|
|
}
|
|
return new Response("Not found", { status: 404 });
|
|
},
|
|
websocket: {
|
|
perMessageDeflate: true,
|
|
open(ws) {
|
|
// Send a message
|
|
const result = ws.send("Test message", true);
|
|
if (result <= 0) {
|
|
throw new Error(`Failed to send test message, result: ${result}`);
|
|
}
|
|
},
|
|
message(ws, message) {
|
|
// Echo back with compression
|
|
const result = ws.send(message, true);
|
|
if (result <= 0) {
|
|
throw new Error(`Failed to echo message in compression test, result: ${result}`);
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
const client = new WebSocket(`ws://localhost:${server.port}`);
|
|
|
|
const { promise: openPromise, resolve: resolveOpen, reject: rejectOpen } = Promise.withResolvers<void>();
|
|
client.onopen = () => resolveOpen();
|
|
client.onerror = error => rejectOpen(error);
|
|
|
|
const { promise: messagePromise, resolve: resolveMessage } = Promise.withResolvers<string>();
|
|
client.onmessage = event => resolveMessage(event.data);
|
|
|
|
await openPromise;
|
|
const receivedMessage = await messagePromise;
|
|
expect(receivedMessage).toBe("Test message");
|
|
|
|
// Send a message to test compression
|
|
client.send(Buffer.alloc(1000, "A").toString()); // Should be compressed
|
|
|
|
client.close();
|
|
server.stop();
|
|
});
|