Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
8a32ac895b fix(serve): don't set default content-type for BufferSource response bodies
Per the Fetch spec, BufferSource bodies (ArrayBuffer, TypedArray, DataView)
should not have a default Content-Type header. Previously, Bun.serve()
was injecting `content-type: application/octet-stream` for these bodies
because `getContentType` checked `blob.contentType().len > 0` which
always returned true (InternalBlob defaults to MimeType.other). Changed
to use `blob.hasContentTypeFromUser()` so only user-specified content
types are applied.

Closes #21193

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-20 04:59:43 +00:00
2 changed files with 91 additions and 5 deletions

View File

@@ -2639,16 +2639,18 @@ fn getContentType(headers: ?*WebCore.FetchHeaders, blob: *const WebCore.Blob.Any
}
}
break :brk if (blob.contentType().len > 0)
break :brk if (blob.hasContentTypeFromUser())
MimeType.byName(blob.contentType())
else if (MimeType.sniff(blob.slice())) |content|
content
else if (blob.wasString())
MimeType.text
// TODO: should we get the mime type off of the Blob.Store if it exists?
// A little wary of doing this right now due to causing some breaking change
else
MimeType.other;
else {
// Per the Fetch spec, BufferSource bodies (ArrayBuffer, TypedArray, DataView)
// should not have a default Content-Type header set.
needs_content_type = false;
break :brk MimeType.other;
};
};
return .{ content_type, needs_content_type, content_type_needs_free };

View File

@@ -0,0 +1,84 @@
import { expect, test } from "bun:test";
// https://github.com/oven-sh/bun/issues/21193
// Per the Fetch spec, BufferSource bodies (ArrayBuffer, TypedArray, DataView)
// should not have a default Content-Type header.
test("Response with Uint8Array body should not have content-type in Bun.serve", async () => {
using server = Bun.serve({
port: 0,
fetch() {
return new Response(new TextEncoder().encode("hello"));
},
});
const res = await fetch(server.url);
expect(res.headers.get("content-type")).toBeNull();
expect(await res.text()).toBe("hello");
});
test("Response with ArrayBuffer body should not have content-type in Bun.serve", async () => {
using server = Bun.serve({
port: 0,
fetch() {
return new Response(new ArrayBuffer(8));
},
});
const res = await fetch(server.url);
expect(res.headers.get("content-type")).toBeNull();
expect(res.headers.get("content-length")).toBe("8");
});
test("Response with DataView body should not have content-type in Bun.serve", async () => {
using server = Bun.serve({
port: 0,
fetch() {
return new Response(new DataView(new ArrayBuffer(4)));
},
});
const res = await fetch(server.url);
expect(res.headers.get("content-type")).toBeNull();
expect(res.headers.get("content-length")).toBe("4");
});
test("Response with string body should still have text/plain content-type", async () => {
using server = Bun.serve({
port: 0,
fetch() {
return new Response("hello");
},
});
const res = await fetch(server.url);
expect(res.headers.get("content-type")).toBe("text/plain;charset=utf-8");
expect(await res.text()).toBe("hello");
});
test("Response with Blob body should have blob content-type", async () => {
using server = Bun.serve({
port: 0,
fetch() {
return new Response(new Blob(["hello"], { type: "text/html" }));
},
});
const res = await fetch(server.url);
expect(res.headers.get("content-type")).toBe("text/html;charset=utf-8");
expect(await res.text()).toBe("hello");
});
test("Response with explicit content-type header and Uint8Array body should keep it", async () => {
using server = Bun.serve({
port: 0,
fetch() {
return new Response(new TextEncoder().encode("hello"), {
headers: { "content-type": "text/plain" },
});
},
});
const res = await fetch(server.url);
expect(res.headers.get("content-type")).toBe("text/plain");
expect(await res.text()).toBe("hello");
});