mirror of
https://github.com/oven-sh/bun
synced 2026-02-13 20:39:05 +00:00
Optimize TLS certificate store creation by avoiding redundant parsing
This removes an expensive call to X509_STORE_set_default_paths() which was parsing certificates from disk on every TLS connection with custom options. Since we already have all certificates parsed and loaded in memory, we can skip the expensive disk I/O and ASN.1 parsing entirely by directly adding our pre-parsed certificates to the X509_STORE. This eliminates the expensive callstack: - cbs_get_any_asn1_element - CBS_get_any_asn1_element - ASN1_get_object - ... (30+ ASN.1 parsing functions) - X509_STORE_set_default_paths The optimization maintains full compatibility while significantly reducing overhead for TLS connections, especially when using custom CA certificates. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -157,10 +157,17 @@ extern "C" X509_STORE *us_get_default_ca_store() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!X509_STORE_set_default_paths(store)) {
|
||||
X509_STORE_free(store);
|
||||
return NULL;
|
||||
}
|
||||
// OPTIMIZATION: We skip X509_STORE_set_default_paths() here!
|
||||
// That function loads certificates from disk and parses them, which is
|
||||
// extremely expensive as shown in the callstack. Since we already have
|
||||
// all the certificates parsed and loaded in memory, we just add them
|
||||
// directly without calling X509_STORE_set_default_paths().
|
||||
//
|
||||
// X509_STORE_set_default_paths() does two things:
|
||||
// 1. Adds a file lookup (X509_LOOKUP_file) for loading certs from files
|
||||
// 2. Adds a hash_dir lookup (X509_LOOKUP_hash_dir) for loading from directories
|
||||
//
|
||||
// We don't need either because we're adding all certificates directly.
|
||||
|
||||
us_default_ca_certificates *default_ca_certificates = us_get_default_ca_certificates();
|
||||
X509** root_cert_instances = default_ca_certificates->root_cert_instances;
|
||||
|
||||
87
test/regression/issue/tls-store-optimization.test.ts
Normal file
87
test/regression/issue/tls-store-optimization.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { test, expect } from "bun:test";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
test("TLS certificate store optimization - avoid redundant X509_STORE_set_default_paths", async () => {
|
||||
// This test verifies that we don't call the expensive X509_STORE_set_default_paths()
|
||||
// function when creating X509_STORE instances. That function parses certificates from
|
||||
// disk which is unnecessary since we already have them parsed in memory.
|
||||
|
||||
// Create a simple HTTPS server
|
||||
const server = Bun.serve({
|
||||
port: 0,
|
||||
tls: {
|
||||
cert: Bun.file("test/js/bun/http/fixtures/cert.pem"),
|
||||
key: Bun.file("test/js/bun/http/fixtures/cert.key"),
|
||||
},
|
||||
fetch(req) {
|
||||
return new Response("OK");
|
||||
},
|
||||
});
|
||||
|
||||
const url = `https://localhost:${server.port}`;
|
||||
const ca = readFileSync("test/js/bun/http/fixtures/cert.pem", "utf8");
|
||||
|
||||
try {
|
||||
// Make multiple requests with custom CA - this used to trigger expensive
|
||||
// X509_STORE_set_default_paths() on each request
|
||||
const promises = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
promises.push(
|
||||
fetch(url, {
|
||||
tls: {
|
||||
ca,
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
}).then(r => r.text())
|
||||
);
|
||||
}
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
// Verify all requests succeeded
|
||||
for (const result of results) {
|
||||
expect(result).toBe("OK");
|
||||
}
|
||||
|
||||
// The optimization removes the expensive callstack:
|
||||
// cbs_get_any_asn1_element -> CBS_get_any_asn1_element -> ... -> X509_STORE_set_default_paths
|
||||
// by skipping X509_STORE_set_default_paths entirely since we already have certificates in memory
|
||||
|
||||
} finally {
|
||||
server.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test("TLS works with NODE_EXTRA_CA_CERTS environment variable", async () => {
|
||||
// Verify that NODE_EXTRA_CA_CERTS still works after our optimization
|
||||
const server = Bun.serve({
|
||||
port: 0,
|
||||
tls: {
|
||||
cert: Bun.file("test/js/bun/http/fixtures/cert.pem"),
|
||||
key: Bun.file("test/js/bun/http/fixtures/cert.key"),
|
||||
},
|
||||
fetch(req) {
|
||||
return new Response("EXTRA_CA_OK");
|
||||
},
|
||||
});
|
||||
|
||||
const url = `https://localhost:${server.port}`;
|
||||
|
||||
try {
|
||||
// Set NODE_EXTRA_CA_CERTS to load additional certificates
|
||||
process.env.NODE_EXTRA_CA_CERTS = "test/js/bun/http/fixtures/cert.pem";
|
||||
|
||||
const response = await fetch(url, {
|
||||
tls: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
});
|
||||
|
||||
expect(await response.text()).toBe("EXTRA_CA_OK");
|
||||
|
||||
} finally {
|
||||
delete process.env.NODE_EXTRA_CA_CERTS;
|
||||
server.stop();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user