aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-10-30 12:35:55 -0700
committerGravatar GitHub <noreply@github.com> 2023-10-30 12:35:55 -0700
commit2972cfadfb7904d6e0e13f586ffc13bac784e52b (patch)
tree5378991748fcc06c48a96c3f1a7f795c7b0f52e6
parent6be17538e7a8d9555ac8becb62fb459539ba0a0f (diff)
downloadbun-2972cfadfb7904d6e0e13f586ffc13bac784e52b.tar.gz
bun-2972cfadfb7904d6e0e13f586ffc13bac784e52b.tar.zst
bun-2972cfadfb7904d6e0e13f586ffc13bac784e52b.zip
More resilient test for fetch body memory leak (#6794)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r--test/js/web/fetch/fetch-leak-test-fixture-2.js37
-rw-r--r--test/js/web/fetch/fetch-leak.test.js65
2 files changed, 86 insertions, 16 deletions
diff --git a/test/js/web/fetch/fetch-leak-test-fixture-2.js b/test/js/web/fetch/fetch-leak-test-fixture-2.js
index 7379b1b7b..26cbdfdb7 100644
--- a/test/js/web/fetch/fetch-leak-test-fixture-2.js
+++ b/test/js/web/fetch/fetch-leak-test-fixture-2.js
@@ -6,21 +6,48 @@ if (typeof SERVER === "undefined" || !SERVER?.length) {
throw new Error("SERVER environment variable is not set");
}
-const COUNT = parseInt(process.env.COUNT || "20", 10);
+const COUNT = parseInt(process.env.COUNT || "50", 10);
var oks = 0;
-await (async function runAll() {
- for (let j = 0; j < COUNT; j++) {
- oks += (await fetch(SERVER)).ok;
+var textLength = 0;
+Bun.gc(true);
+const baseline = await (async function runAll() {
+ const resp = await fetch(SERVER);
+ textLength = Number(resp.headers.get("Content-Length"));
+ if (!textLength) {
+ throw new Error("Content-Length header is not set");
}
+ (await resp.arrayBuffer()).byteLength;
+ return process.memoryUsage.rss();
})();
+Bun.gc(true);
+
+for (let j = 0; j < COUNT; j++) {
+ await (async function runAll() {
+ oks += !!(await (await fetch(SERVER)).arrayBuffer())?.byteLength;
+ })();
+}
+
if (oks !== COUNT) {
throw new Error("Not all requests succeeded");
}
await Bun.sleep(10);
Bun.gc(true);
-
+const delta = process.memoryUsage.rss() - baseline;
if ((heapStats().objectTypeCounts.Response ?? 0) > 5) {
throw new Error("Too many Response objects: " + heapStats().objectTypeCounts.Response);
}
+
+const bodiesLeakedPerRequest = delta / textLength;
+
+const threshold = textLength > 1024 * 1024 * 2 ? 10 : 1000;
+
+console.log({ delta, count: COUNT, bodySize: textLength, bodiesLeakedPerRequest, threshold });
+
+if (bodiesLeakedPerRequest > threshold) {
+ console.log("\n--fail--\n");
+ process.exit(1);
+} else {
+ console.log("\n--pass--\n");
+}
diff --git a/test/js/web/fetch/fetch-leak.test.js b/test/js/web/fetch/fetch-leak.test.js
index 9df32b92a..d296860ff 100644
--- a/test/js/web/fetch/fetch-leak.test.js
+++ b/test/js/web/fetch/fetch-leak.test.js
@@ -32,21 +32,52 @@ describe("fetch doesn't leak", () => {
expect(count).toBe(200);
});
- test("fixture #2", async () => {
- const body = new Blob(["some body in here!".repeat(100)]);
- const server = Bun.serve({
- port: 0,
+ // This tests for body leakage and Response object leakage.
+ async function runTest(compressed, tls) {
+ const body = !compressed
+ ? new Blob(["some body in here!".repeat(2000000)])
+ : new Blob([Bun.deflateSync(crypto.getRandomValues(new Buffer(65123)))]);
+ const headers = {
+ "Content-Type": "application/octet-stream",
+ };
+ if (compressed) {
+ headers["Content-Encoding"] = "deflate";
+ }
+ const serveOptions = {
+ port: 0,
fetch(req) {
- return new Response(body);
+ return new Response(body, { headers });
},
- });
+ };
+
+ if (tls) {
+ const COMMON_CERT = {
+ cert: "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKLdQVPy90jjMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\nBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\naWRnaXRzIFB0eSBMdGQwHhcNMTkwMjAzMTQ0OTM1WhcNMjAwMjAzMTQ0OTM1WjBF\nMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\nZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEA7i7IIEdICTiSTVx+ma6xHxOtcbd6wGW3nkxlCkJ1UuV8NmY5ovMsGnGD\nhJJtUQ2j5ig5BcJUf3tezqCNW4tKnSOgSISfEAKvpn2BPvaFq3yx2Yjz0ruvcGKp\nDMZBXmB/AAtGyN/UFXzkrcfppmLHJTaBYGG6KnmU43gPkSDy4iw46CJFUOupc51A\nFIz7RsE7mbT1plCM8e75gfqaZSn2k+Wmy+8n1HGyYHhVISRVvPqkS7gVLSVEdTea\nUtKP1Vx/818/HDWk3oIvDVWI9CFH73elNxBkMH5zArSNIBTehdnehyAevjY4RaC/\nkK8rslO3e4EtJ9SnA4swOjCiqAIQEwIDAQABo1AwTjAdBgNVHQ4EFgQUv5rc9Smm\n9c4YnNf3hR49t4rH4yswHwYDVR0jBBgwFoAUv5rc9Smm9c4YnNf3hR49t4rH4ysw\nDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATcL9CAAXg0u//eYUAlQa\nL+l8yKHS1rsq1sdmx7pvsmfZ2g8ONQGfSF3TkzkI2OOnCBokeqAYuyT8awfdNUtE\nEHOihv4ZzhK2YZVuy0fHX2d4cCFeQpdxno7aN6B37qtsLIRZxkD8PU60Dfu9ea5F\nDDynnD0TUabna6a0iGn77yD8GPhjaJMOz3gMYjQFqsKL252isDVHEDbpVxIzxPmN\nw1+WK8zRNdunAcHikeoKCuAPvlZ83gDQHp07dYdbuZvHwGj0nfxBLc9qt90XsBtC\n4IYR7c/bcLMmKXYf0qoQ4OzngsnPI5M+v9QEHvYWaKVwFY4CTcSNJEwfXw+BAeO5\nOA==\n-----END CERTIFICATE-----",
+ key: "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDuLsggR0gJOJJN\nXH6ZrrEfE61xt3rAZbeeTGUKQnVS5Xw2Zjmi8ywacYOEkm1RDaPmKDkFwlR/e17O\noI1bi0qdI6BIhJ8QAq+mfYE+9oWrfLHZiPPSu69wYqkMxkFeYH8AC0bI39QVfOSt\nx+mmYsclNoFgYboqeZTjeA+RIPLiLDjoIkVQ66lznUAUjPtGwTuZtPWmUIzx7vmB\n+pplKfaT5abL7yfUcbJgeFUhJFW8+qRLuBUtJUR1N5pS0o/VXH/zXz8cNaTegi8N\nVYj0IUfvd6U3EGQwfnMCtI0gFN6F2d6HIB6+NjhFoL+QryuyU7d7gS0n1KcDizA6\nMKKoAhATAgMBAAECggEAd5g/3o1MK20fcP7PhsVDpHIR9faGCVNJto9vcI5cMMqP\n6xS7PgnSDFkRC6EmiLtLn8Z0k2K3YOeGfEP7lorDZVG9KoyE/doLbpK4MfBAwBG1\nj6AHpbmd5tVzQrnNmuDjBBelbDmPWVbD0EqAFI6mphXPMqD/hFJWIz1mu52Kt2s6\n++MkdqLO0ORDNhKmzu6SADQEcJ9Suhcmv8nccMmwCsIQAUrfg3qOyqU4//8QB8ZM\njosO3gMUesihVeuF5XpptFjrAliPgw9uIG0aQkhVbf/17qy0XRi8dkqXj3efxEDp\n1LSqZjBFiqJlFchbz19clwavMF/FhxHpKIhhmkkRSQKBgQD9blaWSg/2AGNhRfpX\nYq+6yKUkUD4jL7pmX1BVca6dXqILWtHl2afWeUorgv2QaK1/MJDH9Gz9Gu58hJb3\nymdeAISwPyHp8euyLIfiXSAi+ibKXkxkl1KQSweBM2oucnLsNne6Iv6QmXPpXtro\nnTMoGQDS7HVRy1on5NQLMPbUBQKBgQDwmN+um8F3CW6ZV1ZljJm7BFAgNyJ7m/5Q\nYUcOO5rFbNsHexStrx/h8jYnpdpIVlxACjh1xIyJ3lOCSAWfBWCS6KpgeO1Y484k\nEYhGjoUsKNQia8UWVt+uWnwjVSDhQjy5/pSH9xyFrUfDg8JnSlhsy0oC0C/PBjxn\nhxmADSLnNwKBgQD2A51USVMTKC9Q50BsgeU6+bmt9aNMPvHAnPf76d5q78l4IlKt\nwMs33QgOExuYirUZSgjRwknmrbUi9QckRbxwOSqVeMOwOWLm1GmYaXRf39u2CTI5\nV9gTMHJ5jnKd4gYDnaA99eiOcBhgS+9PbgKSAyuUlWwR2ciL/4uDzaVeDQKBgDym\nvRSeTRn99bSQMMZuuD5N6wkD/RxeCbEnpKrw2aZVN63eGCtkj0v9LCu4gptjseOu\n7+a4Qplqw3B/SXN5/otqPbEOKv8Shl/PT6RBv06PiFKZClkEU2T3iH27sws2EGru\nw3C3GaiVMxcVewdg1YOvh5vH8ZVlxApxIzuFlDvnAoGAN5w+gukxd5QnP/7hcLDZ\nF+vesAykJX71AuqFXB4Wh/qFY92CSm7ImexWA/L9z461+NKeJwb64Nc53z59oA10\n/3o2OcIe44kddZXQVP6KTZBd7ySVhbtOiK3/pCy+BQRsrC7d71W914DxNWadwZ+a\njtwwKjDzmPwdIXDSQarCx0U=\n-----END PRIVATE KEY-----",
+ passphrase: "1234",
+ };
+ serveOptions.tls = COMMON_CERT;
+ }
+
+ const server = Bun.serve(serveOptions);
+
+ const env = {
+ ...bunEnv,
+ SERVER: `${tls ? "https" : "http"}://${server.hostname}:${server.port}`,
+ BUN_JSC_forceRAMSize: (1024 * 1024 * 64).toString("10"),
+ };
+
+ if (tls) {
+ env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
+ }
+
+ if (compressed) {
+ env.COUNT = "5000";
+ }
const proc = Bun.spawn({
- env: {
- ...bunEnv,
- SERVER: `http://${server.hostname}:${server.port}`,
- },
+ env,
stderr: "inherit",
stdout: "inherit",
cmd: [bunExe(), "--smol", join(import.meta.dir, "fetch-leak-test-fixture-2.js")],
@@ -55,5 +86,17 @@ describe("fetch doesn't leak", () => {
const exitCode = await proc.exited;
server.stop(true);
expect(exitCode).toBe(0);
- });
+ }
+
+ for (let compressed of [true, false]) {
+ describe(compressed ? "compressed" : "uncompressed", () => {
+ for (let tls of [true, false]) {
+ describe(tls ? "tls" : "tcp", () => {
+ test("fixture #2", async () => {
+ await runTest(compressed, tls);
+ }, 100000);
+ });
+ }
+ });
+ }
});