mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
## Summary Fixes a prerequisite issue in #21792 where `Bun.serve()` incorrectly rejected TLS arrays with exactly 1 object. The original issue reports a WebSocket crash with multiple TLS configs, but users first encounter this validation bug that prevents single-element TLS arrays from working at all. ## Root Cause The bug was in `ServerConfig.zig:918` where the condition checked for exactly 1 element and threw an error: ```zig if (value_iter.len == 1) { return global.throwInvalidArguments("tls option expects at least 1 tls object", .{}); } ``` This prevented users from using the syntax: `tls: [{ cert, key, serverName }]` ## Fix Updated the validation logic to: - Empty TLS arrays are ignored (treated as no TLS) - Single-element TLS arrays work correctly for SNI - Multi-element TLS arrays continue to work as before ```zig if (value_iter.len == 0) { // Empty TLS array means no TLS - this is valid } else { // Process the TLS configs... } ``` ## Testing - ✅ All existing SSL tests still pass (16/16) - ✅ New comprehensive regression test with 7 test cases - ✅ Tests cover empty arrays, single configs, multiple configs, and error cases ## Note This fix addresses the validation issue that prevents users from reaching the deeper WebSocket SNI crash mentioned in #21792. The crash itself may require additional investigation, but this fix resolves the immediate blocker that users encounter first. --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
103 lines
3.5 KiB
TypeScript
103 lines
3.5 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { readFileSync } from "fs";
|
|
import { join } from "path";
|
|
|
|
// This test verifies the fix for GitHub issue #21792:
|
|
// SNI TLS array handling was incorrectly rejecting arrays with exactly 1 TLS config
|
|
describe("SNI TLS array handling (issue #21792)", () => {
|
|
// Use existing test certificates from jsonwebtoken tests
|
|
const certDir = join(import.meta.dir, "../../js/third_party/jsonwebtoken");
|
|
const crtA = readFileSync(join(certDir, "pub.pem"), "utf8");
|
|
const keyA = readFileSync(join(certDir, "priv.pem"), "utf8");
|
|
const crtB = crtA; // Reuse same cert for second test server
|
|
const keyB = keyA;
|
|
|
|
test("should accept empty TLS array (no TLS)", () => {
|
|
// Empty array should be treated as no TLS
|
|
using server = Bun.serve({
|
|
port: 0,
|
|
tls: [],
|
|
fetch: () => new Response("Hello"),
|
|
development: true,
|
|
});
|
|
expect(server.url.toString()).toStartWith("http://"); // HTTP, not HTTPS
|
|
});
|
|
|
|
test("should accept single TLS config in array", () => {
|
|
// This was the bug: single TLS config in array was incorrectly rejected
|
|
using server = Bun.serve({
|
|
port: 0,
|
|
tls: [{ cert: crtA, key: keyA, serverName: "serverA.com" }],
|
|
fetch: () => new Response("Hello from serverA"),
|
|
development: true,
|
|
});
|
|
expect(server.url.toString()).toStartWith("https://");
|
|
});
|
|
|
|
test("should accept multiple TLS configs for SNI", () => {
|
|
using server = Bun.serve({
|
|
port: 0,
|
|
tls: [
|
|
{ cert: crtA, key: keyA, serverName: "serverA.com" },
|
|
{ cert: crtB, key: keyB, serverName: "serverB.com" },
|
|
],
|
|
fetch: request => {
|
|
const host = request.headers.get("host") || "unknown";
|
|
return new Response(`Hello from ${host}`);
|
|
},
|
|
development: true,
|
|
});
|
|
expect(server.url.toString()).toStartWith("https://");
|
|
});
|
|
|
|
test("should reject TLS array with missing serverName for SNI configs", () => {
|
|
expect(() => {
|
|
Bun.serve({
|
|
port: 0,
|
|
tls: [
|
|
{ cert: crtA, key: keyA, serverName: "serverA.com" },
|
|
{ cert: crtB, key: keyB }, // Missing serverName
|
|
],
|
|
fetch: () => new Response("Hello"),
|
|
development: true,
|
|
});
|
|
}).toThrow("SNI tls object must have a serverName");
|
|
});
|
|
|
|
test("should reject TLS array with empty serverName for SNI configs", () => {
|
|
expect(() => {
|
|
Bun.serve({
|
|
port: 0,
|
|
tls: [
|
|
{ cert: crtA, key: keyA, serverName: "serverA.com" },
|
|
{ cert: crtB, key: keyB, serverName: "" }, // Empty serverName
|
|
],
|
|
fetch: () => new Response("Hello"),
|
|
development: true,
|
|
});
|
|
}).toThrow("SNI tls object must have a serverName");
|
|
});
|
|
|
|
test("should accept single TLS config without serverName when alone", () => {
|
|
// When there's only one TLS config in the array, serverName is optional
|
|
using server = Bun.serve({
|
|
port: 0,
|
|
tls: [{ cert: crtA, key: keyA }], // No serverName - should work for single config
|
|
fetch: () => new Response("Hello from default"),
|
|
development: true,
|
|
});
|
|
expect(server.url.toString()).toStartWith("https://");
|
|
});
|
|
|
|
test("should support traditional non-array TLS config", () => {
|
|
// Traditional single TLS config (not in array) should still work
|
|
using server = Bun.serve({
|
|
port: 0,
|
|
tls: { cert: crtA, key: keyA },
|
|
fetch: () => new Response("Hello traditional"),
|
|
development: true,
|
|
});
|
|
expect(server.url.toString()).toStartWith("https://");
|
|
});
|
|
});
|