mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
225 lines
6.4 KiB
TypeScript
225 lines
6.4 KiB
TypeScript
import { Socket } from "bun";
|
|
import { beforeAll, expect, it } from "bun:test";
|
|
import { gcTick } from "harness";
|
|
import path from "path";
|
|
|
|
const gzipped = path.join(import.meta.dir, "fixture.html.gz");
|
|
const html = path.join(import.meta.dir, "fixture.html");
|
|
let htmlText: string;
|
|
beforeAll(async () => {
|
|
htmlText = (await Bun.file(html).text()).replace(/\r\n/g, "\n");
|
|
});
|
|
|
|
it("fetch() with a buffered gzip response works (one chunk)", async () => {
|
|
using server = Bun.serve({
|
|
port: 0,
|
|
|
|
async fetch(req) {
|
|
gcTick(true);
|
|
return new Response(require("fs").readFileSync(gzipped), {
|
|
headers: {
|
|
"Content-Encoding": "gzip",
|
|
"Content-Type": "text/html; charset=utf-8",
|
|
},
|
|
});
|
|
},
|
|
});
|
|
gcTick(true);
|
|
|
|
const res = await fetch(server.url, { verbose: true });
|
|
gcTick(true);
|
|
const arrayBuffer = await res.arrayBuffer();
|
|
const clone = new Buffer(arrayBuffer);
|
|
gcTick(true);
|
|
await (async function () {
|
|
const second = Buffer.from(htmlText);
|
|
gcTick(true);
|
|
expect(second.equals(clone)).toBe(true);
|
|
})();
|
|
gcTick(true);
|
|
});
|
|
|
|
it("fetch() with a redirect that returns a buffered gzip response works (one chunk)", async () => {
|
|
using server = Bun.serve({
|
|
port: 0,
|
|
|
|
async fetch(req) {
|
|
if (req.url.endsWith("/redirect"))
|
|
return new Response(await Bun.file(gzipped).arrayBuffer(), {
|
|
headers: {
|
|
"Content-Encoding": "gzip",
|
|
"Content-Type": "text/html; charset=utf-8",
|
|
},
|
|
});
|
|
|
|
return Response.redirect("/redirect");
|
|
},
|
|
});
|
|
|
|
const url = new URL("hey", server.url);
|
|
const res = await fetch(url, { verbose: true });
|
|
const text = (await res.text()).replace(/\r\n/g, "\n");
|
|
expect(text).toEqual(htmlText);
|
|
});
|
|
|
|
it("fetch() with a protocol-relative redirect that returns a buffered gzip response works (one chunk)", async () => {
|
|
using server = Bun.serve({
|
|
port: 0,
|
|
|
|
async fetch(req, server) {
|
|
if (req.url.endsWith("/redirect"))
|
|
return new Response(await Bun.file(gzipped).arrayBuffer(), {
|
|
headers: {
|
|
"Content-Encoding": "gzip",
|
|
"Content-Type": "text/html; charset=utf-8",
|
|
},
|
|
});
|
|
|
|
const { host } = server.url;
|
|
return Response.redirect(`://${host}/redirect`);
|
|
},
|
|
});
|
|
|
|
const res = await fetch(new URL("hey", server.url), { verbose: true });
|
|
expect(new URL(res.url)).toEqual(new URL("redirect", server.url));
|
|
expect(res.redirected).toBe(true);
|
|
expect(res.status).toBe(200);
|
|
const text = (await res.text()).replace(/\r\n/g, "\n");
|
|
expect(text).toEqual(htmlText);
|
|
});
|
|
|
|
it("fetch() with a gzip response works (one chunk, streamed, with a delay)", async () => {
|
|
using server = Bun.serve({
|
|
port: 0,
|
|
|
|
fetch(req) {
|
|
return new Response(
|
|
new ReadableStream({
|
|
type: "direct",
|
|
async pull(controller) {
|
|
await 2;
|
|
|
|
const buffer = await Bun.file(import.meta.dir + "/fixture.html.gz").arrayBuffer();
|
|
controller.write(buffer);
|
|
controller.close();
|
|
},
|
|
}),
|
|
{
|
|
headers: {
|
|
"Content-Encoding": "gzip",
|
|
"Content-Type": "text/html; charset=utf-8",
|
|
"Content-Length": "1",
|
|
},
|
|
},
|
|
);
|
|
},
|
|
});
|
|
|
|
const res = await fetch(server.url);
|
|
const text = (await res.text()).replace(/\r\n/g, "\n");
|
|
expect(text).toEqual(htmlText);
|
|
});
|
|
|
|
it("fetch() with a gzip response works (multiple chunks, TCP server)", async done => {
|
|
const compressed = await Bun.file(gzipped).arrayBuffer();
|
|
var socketToClose!: Socket;
|
|
let pending,
|
|
pendingChunks = [];
|
|
const server = Bun.listen({
|
|
hostname: "localhost",
|
|
port: 0,
|
|
socket: {
|
|
drain(socket) {
|
|
if (pending) {
|
|
while (pendingChunks.length) {
|
|
const chunk = pendingChunks.shift();
|
|
const written = socket.write(chunk);
|
|
|
|
if (written < chunk.length) {
|
|
pendingChunks.push(chunk.slice(written));
|
|
return;
|
|
}
|
|
}
|
|
const resolv = pending;
|
|
pending = null;
|
|
resolv();
|
|
}
|
|
},
|
|
async open(socket) {
|
|
socketToClose = socket;
|
|
|
|
var corked: any[] = [];
|
|
var cork = true;
|
|
let written = 0;
|
|
let pendingChunks = [];
|
|
async function write(chunk: any) {
|
|
let defer = Promise.withResolvers();
|
|
|
|
if (cork) {
|
|
corked.push(chunk);
|
|
}
|
|
|
|
if (!cork && corked.length) {
|
|
const toWrite = corked.join("");
|
|
const wrote = socket.write(toWrite);
|
|
if (wrote !== toWrite.length) {
|
|
pendingChunks.push(toWrite.slice(wrote));
|
|
}
|
|
corked.length = 0;
|
|
}
|
|
|
|
if (!cork) {
|
|
if (pendingChunks.length) {
|
|
pendingChunks.push(chunk);
|
|
pending = defer.resolve;
|
|
await defer.promise;
|
|
defer = Promise.withResolvers();
|
|
pending = defer.resolve;
|
|
}
|
|
|
|
const written = socket.write(chunk);
|
|
if (written < chunk.length) {
|
|
console.log("written", written);
|
|
pendingChunks.push(chunk.slice(written));
|
|
pending = defer.resolve;
|
|
await defer.promise;
|
|
defer = Promise.withResolvers();
|
|
pending = defer.resolve;
|
|
}
|
|
}
|
|
|
|
const promise = defer.promise;
|
|
if (pendingChunks.length) {
|
|
pending = promise;
|
|
await promise;
|
|
} else {
|
|
pending = null;
|
|
}
|
|
}
|
|
await write("HTTP/1.1 200 OK\r\n");
|
|
await write("Content-Encoding: gzip\r\n");
|
|
await write("Content-Type: text/html; charset=utf-8\r\n");
|
|
await write("Content-Length: " + compressed.byteLength + "\r\n");
|
|
await write("X-WTF: " + "lol".repeat(1000) + "\r\n");
|
|
await write("\r\n");
|
|
for (var i = 100; i < compressed.byteLength; i += 100) {
|
|
cork = false;
|
|
await write(compressed.slice(i - 100, i));
|
|
}
|
|
await write(compressed.slice(i - 100));
|
|
await write("\r\n");
|
|
|
|
socket.flush();
|
|
}
|
|
},
|
|
});
|
|
await 1;
|
|
|
|
const res = await fetch(`http://${server.hostname}:${server.port}`);
|
|
const text = (await res.text()).replace(/\r\n/g, "\n");
|
|
expect(text).toEqual(htmlText);
|
|
socketToClose.end();
|
|
server.stop();
|
|
done();
|
|
});
|