-
Notifications
You must be signed in to change notification settings - Fork 3.9k
fix(node:http): set writableEnded to true when end() called without socket #25685
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix(node:http): set writableEnded to true when end() called without socket #25685
Conversation
…ocket ServerResponse.end() was returning early without setting finished=true when there was no valid socket/handle, causing writableEnded to remain false. Per Node.js spec, writableEnded should always be true after end() is called. Fixes oven-sh#25632 Signed-off-by: lif <[email protected]>
WalkthroughServerResponse.end() was changed to mark the response finished when no underlying handle exists (setting finished/writableEnded), and a regression test was added to verify writableEnded/finished becomes true across several end() scenarios. Changes
Suggested reviewers
Pre-merge checks✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/js/node/_http_server.tstest/regression/issue/25632.test.ts
🧰 Additional context used
📓 Path-based instructions (7)
src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js}
📄 CodeRabbit inference engine (src/js/CLAUDE.md)
src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js}: Use.$call()and.$apply()instead of.call()and.apply()to prevent user tampering with function invocation
Use string literalrequire()statements only; dynamic requires are not permitted
Export modules usingexport default { ... }syntax; modules are NOT ES modules
Use JSC intrinsics (prefixed with$) such as$Array.from(),$isCallable(), and$newArrayWithSize()for performance-critical operations
Use private globals and methods with$prefix (e.g.,$Array,map.$set()) instead of public JavaScript globals
Use$debug()for debug logging and$assert()for assertions; both are stripped in release builds
Validate function arguments using validators frominternal/validatorsand throw$ERR_*error codes for invalid arguments
Useprocess.platformandprocess.archfor platform detection; these values are inlined and dead-code eliminated at build time
Files:
src/js/node/_http_server.ts
src/js/{builtins,node,bun,thirdparty,internal}/**/*.ts
📄 CodeRabbit inference engine (src/js/CLAUDE.md)
Builtin functions must include
thisparameter typing in TypeScript to enable direct method binding in C++
Files:
src/js/node/_http_server.ts
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}: Usebun:testwith files that end in*.test.{ts,js,jsx,tsx,mjs,cjs}
Do not write flaky tests. Never wait for time to pass in tests; always wait for the condition to be met instead of using an arbitrary amount of time
Never use hardcoded port numbers in tests. Always useport: 0to get a random port
Prefer concurrent tests over sequential tests usingtest.concurrentordescribe.concurrentwhen multiple tests spawn processes or write files, unless it's very difficult to make them concurrent
When spawning Bun processes in tests, usebunExeandbunEnvfromharnessto ensure the same build of Bun is used and debug logging is silenced
Use-eflag for single-file tests when spawning Bun processes
UsetempDir()from harness to create temporary directories with files for multi-file tests instead of creating files manually
Prefer async/await over callbacks in tests
When callbacks must be used and it's just a single callback, usePromise.withResolversto create a promise that can be resolved or rejected from a callback
Do not set a timeout on tests. Bun already has timeouts
UseBuffer.alloc(count, fill).toString()instead of'A'.repeat(count)to create repetitive strings in tests, as ''.repeat is very slow in debug JavaScriptCore builds
Usedescribeblocks for grouping related tests
Always useawait usingorusingto ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc
Always check exit codes and test error scenarios in error tests
Usedescribe.each()for parameterized tests
UsetoMatchSnapshot()for snapshot testing
UsebeforeAll(),afterEach(),beforeEach()for setup/teardown in tests
Track resources (servers, clients) in arrays for cleanup inafterEach()
Files:
test/regression/issue/25632.test.ts
test/regression/issue/**/*.test.ts
📄 CodeRabbit inference engine (test/CLAUDE.md)
Regression tests for specific issues go in
/test/regression/issue/${issueNumber}.test.ts. Do not put tests without issue numbers in the regression directory
Files:
test/regression/issue/25632.test.ts
**/*.test.ts?(x)
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.ts?(x): Never usebun testdirectly - always usebun bd testto run tests with debug build changes
For single-file tests, prefer-eflag overtempDir
For multi-file tests, prefertempDirandBun.spawnover single-file tests
UsenormalizeBunSnapshotto normalize snapshot output of tests
Never write tests that check for 'panic', 'uncaught exception', or similar strings in test output
UsetempDirfromharnessto create temporary directories - do not usetmpdirSyncorfs.mkdtempSync
When spawning processes in tests, expect stdout before expecting exit code for more useful error messages on test failure
Do not write flaky tests - do not usesetTimeoutin tests; instead await the condition to be met
Verify tests fail withUSE_SYSTEM_BUN=1 bun test <file>and pass withbun bd test <file>- tests are invalid if they pass with USE_SYSTEM_BUN=1
Test files must end with.test.tsor.test.tsx
Avoid shell commands likefindorgrepin tests - use Bun's Glob and built-in tools instead
Files:
test/regression/issue/25632.test.ts
test/regression/issue/*.test.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Place regression tests for specific GitHub issues in
test/regression/issue/${issueNumber}.test.tswith real issue numbers only
Files:
test/regression/issue/25632.test.ts
test/**/*.test.ts?(x)
📄 CodeRabbit inference engine (CLAUDE.md)
Always use
port: 0in tests - do not hardcode ports or use custom random port number functions
Files:
test/regression/issue/25632.test.ts
🧠 Learnings (13)
📓 Common learnings
Learnt from: cirospaciari
Repo: oven-sh/bun PR: 22756
File: src/js/node/_http_server.ts:945-947
Timestamp: 2025-09-19T21:17:04.690Z
Learning: JSNodeHTTPServerSocket in src/bun.js/bindings/NodeHTTP.cpp implements both end() and close() methods. The end() method properly handles socket termination for both HTTP responses and raw CONNECT sockets by setting ended=true, checking buffered data, and flushing remaining data through the response context.
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/http/node-telemetry.test.ts:27-203
Timestamp: 2025-10-19T04:55:33.099Z
Learning: In test/js/bun/http/node-telemetry.test.ts and the Bun.telemetry._node_binding API, after the architecture refactor, the _node_binding interface only contains two methods: handleIncomingRequest(req, res) and handleWriteHead(res, statusCode). The handleRequestFinish hook and other lifecycle hooks were removed during simplification. Both current methods are fully tested.
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/http/node-telemetry.test.ts:27-88
Timestamp: 2025-10-19T04:55:27.213Z
Learning: In the Bun.telemetry._node_binding API for Node.js http.createServer compatibility, there are exactly two hooks: handleIncomingRequest(req, res) called once per request (returns a request ID), and handleWriteHead(res, statusCode) called once when headers are sent (explicit or implicit). The old handleRequestFinish hook no longer exists after architectural refactoring. Both hooks are tested in test/js/bun/http/node-telemetry.test.ts.
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add corresponding test cases to test/v8/v8.test.ts using checkSameOutput() function to compare Node.js and Bun output
📚 Learning: 2025-09-19T21:17:04.690Z
Learnt from: cirospaciari
Repo: oven-sh/bun PR: 22756
File: src/js/node/_http_server.ts:945-947
Timestamp: 2025-09-19T21:17:04.690Z
Learning: JSNodeHTTPServerSocket in src/bun.js/bindings/NodeHTTP.cpp implements both end() and close() methods. The end() method properly handles socket termination for both HTTP responses and raw CONNECT sockets by setting ended=true, checking buffered data, and flushing remaining data through the response context.
Applied to files:
src/js/node/_http_server.tstest/regression/issue/25632.test.ts
📚 Learning: 2025-10-19T04:55:27.213Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/http/node-telemetry.test.ts:27-88
Timestamp: 2025-10-19T04:55:27.213Z
Learning: In the Bun.telemetry._node_binding API for Node.js http.createServer compatibility, there are exactly two hooks: handleIncomingRequest(req, res) called once per request (returns a request ID), and handleWriteHead(res, statusCode) called once when headers are sent (explicit or implicit). The old handleRequestFinish hook no longer exists after architectural refactoring. Both hooks are tested in test/js/bun/http/node-telemetry.test.ts.
Applied to files:
src/js/node/_http_server.ts
📚 Learning: 2025-10-19T04:55:33.099Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/http/node-telemetry.test.ts:27-203
Timestamp: 2025-10-19T04:55:33.099Z
Learning: In test/js/bun/http/node-telemetry.test.ts and the Bun.telemetry._node_binding API, after the architecture refactor, the _node_binding interface only contains two methods: handleIncomingRequest(req, res) and handleWriteHead(res, statusCode). The handleRequestFinish hook and other lifecycle hooks were removed during simplification. Both current methods are fully tested.
Applied to files:
src/js/node/_http_server.tstest/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/regression/issue/**/*.test.ts : Regression tests for specific issues go in `/test/regression/issue/${issueNumber}.test.ts`. Do not put tests without issue numbers in the regression directory
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add corresponding test cases to test/v8/v8.test.ts using checkSameOutput() function to compare Node.js and Bun output
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-12-16T00:21:32.179Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T00:21:32.179Z
Learning: Applies to test/regression/issue/*.test.ts : Place regression tests for specific GitHub issues in `test/regression/issue/${issueNumber}.test.ts` with real issue numbers only
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-10-18T05:23:24.403Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/telemetry-server.test.ts:91-100
Timestamp: 2025-10-18T05:23:24.403Z
Learning: In the Bun codebase, telemetry tests (test/js/bun/telemetry-*.test.ts) should focus on telemetry API behavior: configure/disable/isEnabled, callback signatures and invocation, request ID correlation, and error handling. HTTP protocol behaviors like status code normalization (e.g., 200 with empty body → 204) should be tested in HTTP server tests (test/js/bun/http/), not in telemetry tests. Keep separation of concerns: telemetry tests verify the telemetry API contract; HTTP tests verify HTTP semantics.
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*-fixture.ts : Test files that spawn Bun processes should end in `*-fixture.ts` to identify them as test fixtures and not tests themselves
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Track resources (servers, clients) in arrays for cleanup in `afterEach()`
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-12-16T00:21:32.179Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T00:21:32.179Z
Learning: Applies to **/*.test.ts?(x) : When spawning processes in tests, expect stdout before expecting exit code for more useful error messages on test failure
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always check exit codes and test error scenarios in error tests
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always use `await using` or `using` to ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc
Applied to files:
test/regression/issue/25632.test.ts
🧬 Code graph analysis (1)
test/regression/issue/25632.test.ts (1)
test/js/node/test/parallel/test-http-outgoing-proto.js (1)
ServerResponse(8-8)
🔇 Additional comments (4)
src/js/node/_http_server.ts (1)
1335-1336: LGTM! Correct fix for writableEnded behavior.The fix correctly sets
finished = truewhenend()is called without a valid handle, ensuringwritableEndedreturns true as per Node.js spec. This makes the behavior consistent with the code path at line 1377 wherefinishedis also set to true when a handle exists.test/regression/issue/25632.test.ts (3)
11-24: LGTM! Comprehensive test for the fix.This test directly validates that
writableEndedandfinishedbecome true after callingend()without a valid socket, which is the core issue being fixed.
26-41: LGTM! Validates callback behavior.This test ensures that the callback handling via
process.nextTickstill works correctly after the fix, and thatwritableEndedbecomes true immediately whenend()is called.
43-51: LGTM! Covers edge case with data chunk.This test validates that passing data to
end()doesn't preventwritableEndedfrom becoming true when there's no socket.
Jarred-Sumner
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we rewrite the test using node:test and try running it with node, the test should be true after end() is called with callback but without socket fails in node.
/**
* Regression test for issue #25632
* ServerResponse.end() should always result in writableEnded being set to/returning true
*
* @see https://github.com/oven-sh/bun/issues/25632
*/
import assert from "node:assert";
import http, { createServer, ServerResponse } from "node:http";
import { describe, test } from "node:test";
describe("ServerResponse.writableEnded", () => {
test("should be true after end() is called without a socket", async () => {
// Create a ServerResponse without a valid socket/handle
const req = new http.IncomingMessage(null as any);
const res = new ServerResponse(req);
assert.strictEqual(res.writableEnded, false);
assert.strictEqual(res.finished, false);
res.end();
// Per Node.js spec, writableEnded should be true after end() is called
assert.strictEqual(res.writableEnded, true);
assert.strictEqual(res.finished, true);
});
test("should be true after end() is called with callback but without socket", async () => {
const req = new http.IncomingMessage(null as any);
const res = new ServerResponse(req);
let callbackCalled = false;
res.end(() => {
callbackCalled = true;
});
assert.strictEqual(res.writableEnded, true);
assert.strictEqual(res.finished, true);
// Wait for callback to be called via nextTick
await new Promise(resolve => process.nextTick(resolve));
assert.strictEqual(callbackCalled, true);
});
test("should be true after end() with chunk but without socket", async () => {
const req = new http.IncomingMessage(null as any);
const res = new ServerResponse(req);
res.end("test data");
assert.strictEqual(res.writableEnded, true);
assert.strictEqual(res.finished, true);
});
test("should be true in normal server context", async () => {
const server = createServer((req, res) => {
assert.strictEqual(res.writableEnded, false);
res.end("Hello");
assert.strictEqual(res.writableEnded, true);
});
server.listen({ port: 0 });
const { port } = server.address() as { port: number };
try {
const response = await fetch(`http://localhost:${port}`);
assert.strictEqual(await response.text(), "Hello");
} finally {
server.close();
}
});
});There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/js/node/_http_server.tstest/regression/issue/25632.test.ts
🧰 Additional context used
📓 Path-based instructions (7)
src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js}
📄 CodeRabbit inference engine (src/js/CLAUDE.md)
src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js}: Use.$call()and.$apply()instead of.call()and.apply()to prevent user tampering with function invocation
Use string literalrequire()statements only; dynamic requires are not permitted
Export modules usingexport default { ... }syntax; modules are NOT ES modules
Use JSC intrinsics (prefixed with$) such as$Array.from(),$isCallable(), and$newArrayWithSize()for performance-critical operations
Use private globals and methods with$prefix (e.g.,$Array,map.$set()) instead of public JavaScript globals
Use$debug()for debug logging and$assert()for assertions; both are stripped in release builds
Validate function arguments using validators frominternal/validatorsand throw$ERR_*error codes for invalid arguments
Useprocess.platformandprocess.archfor platform detection; these values are inlined and dead-code eliminated at build time
Files:
src/js/node/_http_server.ts
src/js/{builtins,node,bun,thirdparty,internal}/**/*.ts
📄 CodeRabbit inference engine (src/js/CLAUDE.md)
Builtin functions must include
thisparameter typing in TypeScript to enable direct method binding in C++
Files:
src/js/node/_http_server.ts
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}: Usebun:testwith files that end in*.test.{ts,js,jsx,tsx,mjs,cjs}
Do not write flaky tests. Never wait for time to pass in tests; always wait for the condition to be met instead of using an arbitrary amount of time
Never use hardcoded port numbers in tests. Always useport: 0to get a random port
Prefer concurrent tests over sequential tests usingtest.concurrentordescribe.concurrentwhen multiple tests spawn processes or write files, unless it's very difficult to make them concurrent
When spawning Bun processes in tests, usebunExeandbunEnvfromharnessto ensure the same build of Bun is used and debug logging is silenced
Use-eflag for single-file tests when spawning Bun processes
UsetempDir()from harness to create temporary directories with files for multi-file tests instead of creating files manually
Prefer async/await over callbacks in tests
When callbacks must be used and it's just a single callback, usePromise.withResolversto create a promise that can be resolved or rejected from a callback
Do not set a timeout on tests. Bun already has timeouts
UseBuffer.alloc(count, fill).toString()instead of'A'.repeat(count)to create repetitive strings in tests, as ''.repeat is very slow in debug JavaScriptCore builds
Usedescribeblocks for grouping related tests
Always useawait usingorusingto ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc
Always check exit codes and test error scenarios in error tests
Usedescribe.each()for parameterized tests
UsetoMatchSnapshot()for snapshot testing
UsebeforeAll(),afterEach(),beforeEach()for setup/teardown in tests
Track resources (servers, clients) in arrays for cleanup inafterEach()
Files:
test/regression/issue/25632.test.ts
test/regression/issue/**/*.test.ts
📄 CodeRabbit inference engine (test/CLAUDE.md)
Regression tests for specific issues go in
/test/regression/issue/${issueNumber}.test.ts. Do not put tests without issue numbers in the regression directory
Files:
test/regression/issue/25632.test.ts
**/*.test.ts?(x)
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.ts?(x): Never usebun testdirectly - always usebun bd testto run tests with debug build changes
For single-file tests, prefer-eflag overtempDir
For multi-file tests, prefertempDirandBun.spawnover single-file tests
UsenormalizeBunSnapshotto normalize snapshot output of tests
Never write tests that check for 'panic', 'uncaught exception', or similar strings in test output
UsetempDirfromharnessto create temporary directories - do not usetmpdirSyncorfs.mkdtempSync
When spawning processes in tests, expect stdout before expecting exit code for more useful error messages on test failure
Do not write flaky tests - do not usesetTimeoutin tests; instead await the condition to be met
Verify tests fail withUSE_SYSTEM_BUN=1 bun test <file>and pass withbun bd test <file>- tests are invalid if they pass with USE_SYSTEM_BUN=1
Test files must end with.test.tsor.test.tsx
Avoid shell commands likefindorgrepin tests - use Bun's Glob and built-in tools instead
Files:
test/regression/issue/25632.test.ts
test/regression/issue/*.test.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Place regression tests for specific GitHub issues in
test/regression/issue/${issueNumber}.test.tswith real issue numbers only
Files:
test/regression/issue/25632.test.ts
test/**/*.test.ts?(x)
📄 CodeRabbit inference engine (CLAUDE.md)
Always use
port: 0in tests - do not hardcode ports or use custom random port number functions
Files:
test/regression/issue/25632.test.ts
🧠 Learnings (19)
📓 Common learnings
Learnt from: cirospaciari
Repo: oven-sh/bun PR: 22756
File: src/js/node/_http_server.ts:945-947
Timestamp: 2025-09-19T21:17:04.690Z
Learning: JSNodeHTTPServerSocket in src/bun.js/bindings/NodeHTTP.cpp implements both end() and close() methods. The end() method properly handles socket termination for both HTTP responses and raw CONNECT sockets by setting ended=true, checking buffered data, and flushing remaining data through the response context.
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/http/node-telemetry.test.ts:27-203
Timestamp: 2025-10-19T04:55:33.099Z
Learning: In test/js/bun/http/node-telemetry.test.ts and the Bun.telemetry._node_binding API, after the architecture refactor, the _node_binding interface only contains two methods: handleIncomingRequest(req, res) and handleWriteHead(res, statusCode). The handleRequestFinish hook and other lifecycle hooks were removed during simplification. Both current methods are fully tested.
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/http/node-telemetry.test.ts:27-88
Timestamp: 2025-10-19T04:55:27.213Z
Learning: In the Bun.telemetry._node_binding API for Node.js http.createServer compatibility, there are exactly two hooks: handleIncomingRequest(req, res) called once per request (returns a request ID), and handleWriteHead(res, statusCode) called once when headers are sent (explicit or implicit). The old handleRequestFinish hook no longer exists after architectural refactoring. Both hooks are tested in test/js/bun/http/node-telemetry.test.ts.
📚 Learning: 2025-09-19T21:17:04.690Z
Learnt from: cirospaciari
Repo: oven-sh/bun PR: 22756
File: src/js/node/_http_server.ts:945-947
Timestamp: 2025-09-19T21:17:04.690Z
Learning: JSNodeHTTPServerSocket in src/bun.js/bindings/NodeHTTP.cpp implements both end() and close() methods. The end() method properly handles socket termination for both HTTP responses and raw CONNECT sockets by setting ended=true, checking buffered data, and flushing remaining data through the response context.
Applied to files:
src/js/node/_http_server.tstest/regression/issue/25632.test.ts
📚 Learning: 2025-10-19T04:55:33.099Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/http/node-telemetry.test.ts:27-203
Timestamp: 2025-10-19T04:55:33.099Z
Learning: In test/js/bun/http/node-telemetry.test.ts and the Bun.telemetry._node_binding API, after the architecture refactor, the _node_binding interface only contains two methods: handleIncomingRequest(req, res) and handleWriteHead(res, statusCode). The handleRequestFinish hook and other lifecycle hooks were removed during simplification. Both current methods are fully tested.
Applied to files:
src/js/node/_http_server.tstest/regression/issue/25632.test.ts
📚 Learning: 2025-10-19T04:55:27.213Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/http/node-telemetry.test.ts:27-88
Timestamp: 2025-10-19T04:55:27.213Z
Learning: In the Bun.telemetry._node_binding API for Node.js http.createServer compatibility, there are exactly two hooks: handleIncomingRequest(req, res) called once per request (returns a request ID), and handleWriteHead(res, statusCode) called once when headers are sent (explicit or implicit). The old handleRequestFinish hook no longer exists after architectural refactoring. Both hooks are tested in test/js/bun/http/node-telemetry.test.ts.
Applied to files:
src/js/node/_http_server.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/regression/issue/**/*.test.ts : Regression tests for specific issues go in `/test/regression/issue/${issueNumber}.test.ts`. Do not put tests without issue numbers in the regression directory
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-12-16T00:21:32.179Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T00:21:32.179Z
Learning: Applies to test/regression/issue/*.test.ts : Place regression tests for specific GitHub issues in `test/regression/issue/${issueNumber}.test.ts` with real issue numbers only
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add corresponding test cases to test/v8/v8.test.ts using checkSameOutput() function to compare Node.js and Bun output
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always check exit codes and test error scenarios in error tests
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-12-16T00:21:32.179Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T00:21:32.179Z
Learning: Applies to **/*.test.ts?(x) : When spawning processes in tests, expect stdout before expecting exit code for more useful error messages on test failure
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Track resources (servers, clients) in arrays for cleanup in `afterEach()`
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-10-18T05:23:24.403Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: test/js/bun/telemetry-server.test.ts:91-100
Timestamp: 2025-10-18T05:23:24.403Z
Learning: In the Bun codebase, telemetry tests (test/js/bun/telemetry-*.test.ts) should focus on telemetry API behavior: configure/disable/isEnabled, callback signatures and invocation, request ID correlation, and error handling. HTTP protocol behaviors like status code normalization (e.g., 200 with empty body → 204) should be tested in HTTP server tests (test/js/bun/http/), not in telemetry tests. Keep separation of concerns: telemetry tests verify the telemetry API contract; HTTP tests verify HTTP semantics.
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*-fixture.ts : Test files that spawn Bun processes should end in `*-fixture.ts` to identify them as test fixtures and not tests themselves
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always use `await using` or `using` to ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Never use hardcoded port numbers in tests. Always use `port: 0` to get a random port
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-12-16T00:21:32.179Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T00:21:32.179Z
Learning: Applies to test/**/*.test.ts?(x) : Always use `port: 0` in tests - do not hardcode ports or use custom random port number functions
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-09-03T01:30:58.001Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 21728
File: test/js/valkey/valkey.test.ts:264-271
Timestamp: 2025-09-03T01:30:58.001Z
Learning: For test/js/valkey/valkey.test.ts PUB/SUB tests, avoid arbitrary sleeps and async-forEach. Instead, resolve a Promise from the subscriber callback when the expected number of messages is observed and await it with a bounded timeout (e.g., withTimeout + Promise.withResolvers) to account for Redis server→subscriber propagation.
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-06T00:58:23.965Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 24417
File: test/js/bun/spawn/spawn.test.ts:903-918
Timestamp: 2025-11-06T00:58:23.965Z
Learning: In Bun test files, `await using` with spawn() is appropriate for long-running processes that need guaranteed cleanup on scope exit or when explicitly testing disposal behavior. For short-lived processes that exit naturally (e.g., console.log scripts), the pattern `const proc = spawn(...); await proc.exited;` is standard and more common, as evidenced by 24 instances vs 4 `await using` instances in test/js/bun/spawn/spawn.test.ts.
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-09-02T05:33:37.517Z
Learnt from: Jarred-Sumner
Repo: oven-sh/bun PR: 22323
File: test/js/web/websocket/websocket-subprotocol.test.ts:74-75
Timestamp: 2025-09-02T05:33:37.517Z
Learning: In Bun's runtime, `await using` with Node.js APIs like `net.createServer()` is properly supported and should not be replaced with explicit cleanup. Bun has extended Node.js APIs with proper async dispose support.
Applied to files:
test/regression/issue/25632.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Prefer async/await over callbacks in tests
Applied to files:
test/regression/issue/25632.test.ts
🧬 Code graph analysis (1)
test/regression/issue/25632.test.ts (1)
test/js/node/test/parallel/test-http-outgoing-proto.js (1)
ServerResponse(8-8)
🔇 Additional comments (4)
test/regression/issue/25632.test.ts (3)
11-24: LGTM!The test correctly verifies that
writableEndedandfinishedreturn true afterend()is called without a socket, which is the core fix for issue #25632.
40-48: LGTM!The test correctly verifies that
writableEndedandfinishedare true afterend()is called with a data chunk but without a socket.
50-66: LGTM!The test validates normal server operation and properly cleans up resources. The server cleanup is correctly awaited (line 64), which addresses the past review comment and ensures proper resource management as per coding guidelines.
src/js/node/_http_server.ts (1)
1334-1340: The no-handle behavior correctly matches Node.js.The implementation properly handles the case where there is no socket:
- Sets
finished = trueto makewritableEndedreturntrue- Does not invoke the callback (verified Node.js does the same)
- Does not emit events (verified Node.js emits nothing: prefinish, finish, and close are not triggered)
The code and comments accurately document the intended behavior.
| test("should be true after end() is called with callback but without socket", async () => { | ||
| const req = new http.IncomingMessage(null as any); | ||
| const res = new ServerResponse(req); | ||
|
|
||
| res.end(() => { | ||
| // Note: In Node.js, callback is NOT invoked when there's no socket | ||
| // This matches Node.js behavior where the 'finish' event never fires without a socket | ||
| }); | ||
|
|
||
| // Per Node.js spec, writableEnded should be true after end() is called | ||
| expect(res.writableEnded).toBe(true); | ||
| expect(res.finished).toBe(true); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider explicitly testing that callback is not invoked.
The test passes a callback to end() and documents that it should not be invoked (lines 31-32), but doesn't actually verify this behavior. While the test correctly checks writableEnded and finished, explicitly testing callback non-invocation would make the test more robust and self-documenting.
🔎 Optional enhancement to verify callback behavior
test("should be true after end() is called with callback but without socket", async () => {
const req = new http.IncomingMessage(null as any);
const res = new ServerResponse(req);
+ let callbackInvoked = false;
res.end(() => {
- // Note: In Node.js, callback is NOT invoked when there's no socket
- // This matches Node.js behavior where the 'finish' event never fires without a socket
+ callbackInvoked = true;
});
// Per Node.js spec, writableEnded should be true after end() is called
expect(res.writableEnded).toBe(true);
expect(res.finished).toBe(true);
+
+ // Wait one tick to ensure callback would have been invoked if it were going to be
+ await new Promise(resolve => process.nextTick(resolve));
+ expect(callbackInvoked).toBe(false); // Callback should NOT be invoked without socket
});🤖 Prompt for AI Agents
In test/regression/issue/25632.test.ts around lines 26 to 38, the test documents
that the callback passed to res.end() should not be invoked but never asserts
it; add an explicit assertion by replacing the empty callback with a spy/flag
(e.g., a jest.fn() or a boolean set inside the callback) when calling
res.end(...) then assert that the spy/flag was not called/changed after end()
and after any necessary microtask ticks, keeping the existing expectations for
writableEnded and finished.
- WTFMove → WTF::move / std::move: Replaced WTFMove() macro with WTF::move() function for WTF types, std::move() for std types - SortedArrayMap removed: Replaced with if-else chains in EventFactory.cpp, JSCryptoKeyUsage.cpp - Wasm::Memory::create signature changed: Removed VM parameter - URLPattern allocation: Changed from WTF_MAKE_ISO_ALLOCATED to WTF_MAKE_TZONE_ALLOCATED --------- Co-authored-by: Claude Opus 4.5 <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…characters + fix (oven-sh#25697) ### What does this PR do? - Add test that is broken before the changes in the code and fix previous test making script in dependency takes a bit of time to be executed. Without the `setTimeout` in the tests, due race conditions it always success. I tried adding a test combining both tests, with dependencies `dep0` and `larger-than-8-char`, but if the timeout is the same it success. - Fix for the use case added, by using the correct buffer for `Dependency.name` otherwise it gets garbage when package name is larger than 8 characters. This should fix oven-sh#12203 ### How did you verify your code works? Undo the changes in the code to verify the new test fails and check it again after adding the changes in the code.
## Summary - Remove sccache support entirely, use ccache only - Missing ccache no longer fails the build (just skips caching) - Remove S3 distributed cache support ## Changes - Remove `cmake/tools/SetupSccache.cmake` and S3 distributed cache support - Simplify `CMakeLists.txt` to only use ccache - Update `SetupCcache.cmake` to not fail when ccache is missing - Replace sccache with ccache in bootstrap scripts (sh, ps1) - Update `.buildkite/Dockerfile` to install ccache instead of sccache - Update `flake.nix` and `shell.nix` to use ccache - Update documentation (CONTRIBUTING.md, contributing.mdx, building-windows.mdx) - Remove `scripts/build-cache/` directory (was only for sccache S3 access) ## Test plan - [x] Build completes successfully with `bun bd` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Bot <[email protected]> Co-authored-by: Claude <[email protected]>
…h#25710) ## Summary - Fixes dead code elimination producing invalid syntax like `{ ...a, x: }` when simplifying empty objects in spread contexts - The issue was that `simplifyUnusedExpr` and `joinAllWithCommaCallback` could return `E.Missing` instead of `null` to indicate "no side effects" - Added checks to return `null` when the result is `E.Missing` Fixes oven-sh#25609 ## Test plan - [x] Added regression test that fails on v1.3.5 and passes with fix - [x] `bun bd test test/regression/issue/25609.test.ts` passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <[email protected]>
note: This is due to constant folding by the JIT. For `String#includes` on dynamic strings, the performance improvement is not this significant. **before:** ``` $ bun ./bench/snippets/string-includes.mjs cpu: Apple M4 Max runtime: bun 1.3.5 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p999 ----------------------------------------------------------------------------- ----------------------------- String.includes - short, hit (middle) 82.24 ns/iter (14.95 ns … 881 ns) 84.98 ns 470 ns 791 ns String.includes - short, hit (start) 37.44 ns/iter (8.46 ns … 774 ns) 26.08 ns 379 ns 598 ns String.includes - short, hit (end) 97.27 ns/iter (16.93 ns … 823 ns) 107 ns 537 ns 801 ns String.includes - short, miss 102 ns/iter (0 ps … 1'598 µs) 42 ns 125 ns 167 ns String.includes - long, hit (middle) 16.01 ns/iter (14.34 ns … 115 ns) 16.03 ns 20.1 ns 53.1 ns String.includes - long, miss 945 ns/iter (935 ns … 972 ns) 948 ns 960 ns 972 ns String.includes - with position 9.83 ns/iter (8.44 ns … 58.45 ns) 9.83 ns 12.31 ns 15.69 ns ``` **after:** ``` $ ./build/release/bun bench/snippets/string-includes.mjs cpu: Apple M4 Max runtime: bun 1.3.6 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p999 ----------------------------------------------------------------------------- ----------------------------- String.includes - short, hit (middle) 243 ps/iter (203 ps … 10.13 ns) 244 ps 325 ps 509 ps ! String.includes - short, hit (start) 374 ps/iter (244 ps … 19.78 ns) 387 ps 488 ps 691 ps String.includes - short, hit (end) 708 ps/iter (407 ps … 18.03 ns) 651 ps 2.62 ns 2.69 ns String.includes - short, miss 1.49 ns/iter (407 ps … 27.93 ns) 2.87 ns 3.09 ns 3.78 ns String.includes - long, hit (middle) 3.28 ns/iter (3.05 ns … 118 ns) 3.15 ns 8.75 ns 16.07 ns String.includes - long, miss 7.28 ns/iter (3.44 ns … 698 ns) 9.34 ns 42.85 ns 240 ns String.includes - with position 7.97 ns/iter (3.7 ns … 602 ns) 9.68 ns 52.19 ns 286 ns ```
…sh#25698) ## Summary - Reject null bytes in command-line arguments passed to `Bun.spawn` and `Bun.spawnSync` - Reject null bytes in environment variable keys and values - Reject null bytes in shell (`$`) template literal arguments This prevents null byte injection attacks (CWE-158) where null bytes in strings could cause unintended truncation when passed to the OS, potentially allowing attackers to bypass file extension validation or create files with unexpected names. ## Test plan - [x] Added tests in `test/js/bun/spawn/null-byte-injection.test.ts` - [x] Tests pass with debug build: `bun bd test test/js/bun/spawn/null-byte-injection.test.ts` - [x] Tests fail with system Bun (confirming the fix works) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Bot <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: Jarred Sumner <[email protected]>
## What does this PR do? Switch `Bun.hash.crc32` to use `zlib`'s CRC32 implementation. Bun already links `zlib`, which provides highly optimized, hardware-accelerated CRC32. Because `zlib.crc32` takes a 32-bit length, chunk large inputs to avoid truncation/regressions on buffers >4GB. Note: This was tried before (PR oven-sh#12164 by Jarred), which switched CRC32 to zlib for speed. This proposal keeps that approach and adds explicit chunking to avoid the >4GB length pitfall. **Problem:** `Bun.hash.crc32` is a significant outlier in microbenchmarks compared to other hash functions (about 21x slower than `zlib.crc32` in a 1MB test on M1). **Root cause:** `Bun.hash.crc32` uses Zig's `std.hash.Crc32` implementation, which is software-only and does not leverage hardware acceleration (e.g., `PCLMULQDQ` on x86 or `CRC32` instructions on ARM). **Implementation:** https://github.com/oven-sh/bun/blob/main/src/bun.js/api/HashObject.zig ```zig pub const crc32 = hashWrap(struct { pub fn hash(seed: u32, bytes: []const u8) u32 { // zlib takes a 32-bit length, so chunk large inputs to avoid truncation. var crc: u64 = seed; var offset: usize = 0; while (offset < bytes.len) { const remaining = bytes.len - offset; const max_len: usize = std.math.maxInt(u32); const chunk_len: u32 = if (remaining > max_len) @intcast(max_len) else @intcast(remaining); crc = bun.zlib.crc32(crc, bytes.ptr + offset, chunk_len); offset += chunk_len; } return @intcast(crc); } }); ``` ### How did you verify your code works? **Benchmark (1MB payload):** - **Before:** Bun 1.3.5 `Bun.hash.crc32` = 2,644,444 ns/op vs `zlib.crc32` = 124,324 ns/op (~21x slower) - **After (local bun-debug):** `Bun.hash.crc32` = 360,591 ns/op vs `zlib.crc32` = 359,069 ns/op (~1.0x), results match ## Test environment - **Machine:** MacBook Pro 13" (M1, 2020) - **OS:** macOS 15.7.3 - **Baseline Bun:** 1.3.5 - **After Bun:** local `bun-debug` (build/debug)
**before:** ``` $ bun bench/snippets/array-of.js cpu: Apple M4 Max runtime: bun 1.3.5 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p999 ----------------------------------------------------------------------------- ----------------------------- int: Array.of(1,2,3,4,5) 9.19 ns/iter (8.1 ns … 108 ns) 9.28 ns 13.63 ns 69.44 ns int: Array.of(100 elements) 1'055 ns/iter (94.58 ns … 1'216 ns) 1'108 ns 1'209 ns 1'216 ns double: Array.of(1.1,2.2,3.3,4.4,5.5) 10.34 ns/iter (8.81 ns … 102 ns) 10.17 ns 17.19 ns 73.51 ns double: Array.of(100 elements) 1'073 ns/iter (124 ns … 1'215 ns) 1'136 ns 1'204 ns 1'215 ns object: Array.of(obj x5) 19.19 ns/iter (16.58 ns … 122 ns) 19.06 ns 77.6 ns 85.75 ns object: Array.of(100 elements) 1'340 ns/iter (294 ns … 1'568 ns) 1'465 ns 1'537 ns 1'568 ns ``` **after:** ``` $ ./build/release/bun bench/snippets/array-of.js cpu: Apple M4 Max runtime: bun 1.3.6 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p999 ----------------------------------------------------------------------------- ----------------------------- int: Array.of(1,2,3,4,5) 2.68 ns/iter (2.14 ns … 92.96 ns) 2.52 ns 3.95 ns 59.73 ns int: Array.of(100 elements) 23.69 ns/iter (18.88 ns … 155 ns) 20.91 ns 83.82 ns 96.66 ns double: Array.of(1.1,2.2,3.3,4.4,5.5) 3.62 ns/iter (2.97 ns … 75.44 ns) 3.46 ns 5.05 ns 65.82 ns double: Array.of(100 elements) 26.96 ns/iter (20.14 ns … 156 ns) 24.45 ns 87.75 ns 96.88 ns object: Array.of(obj x5) 11.82 ns/iter (9.6 ns … 87.38 ns) 11.23 ns 68.99 ns 77.09 ns object: Array.of(100 elements) 236 ns/iter (206 ns … 420 ns) 273 ns 325 ns 386 ns ```
…or space (oven-sh#25717) ## Summary - Fix performance regression where `Response.json()` was 2-3x slower than `JSON.stringify() + new Response()` - Root cause: The existing code called `JSC::JSONStringify` with `indent=0`, which internally passes `jsNumber(0)` as the space parameter. This bypasses WebKit's FastStringifier optimization. - Fix: Add a new `jsonStringifyFast` binding that passes `jsUndefined()` for the space parameter, triggering JSC's FastStringifier (SIMD-optimized) code path. ## Root Cause Analysis In WebKit's `JSONObject.cpp`, the `stringify()` function has this logic: ```cpp static NEVER_INLINE String stringify(JSGlobalObject& globalObject, JSValue value, JSValue replacer, JSValue space) { // ... if (String result = FastStringifier<Latin1Character, BufferMode::StaticBuffer>::stringify(globalObject, value, replacer, space, failureReason); !result.isNull()) return result; // Falls back to slow Stringifier... } ``` And `FastStringifier::stringify()` checks: ```cpp if (!space.isUndefined()) { logOutcome("space"_s); return { }; // Bail out to slow path } ``` So when we called `JSONStringify(globalObject, value, (unsigned)0)`, it converted to `jsNumber(0)` which is NOT `undefined`, causing FastStringifier to bail out. ## Performance Results ### Before (3.5x slower than manual approach) ``` Response.json(): 2415ms JSON.stringify() + Response(): 689ms Ratio: 3.50x ``` ### After (parity with manual approach) ``` Response.json(): ~700ms JSON.stringify() + Response(): ~700ms Ratio: ~1.09x ``` ## Test plan - [x] Existing `Response.json()` tests pass (`test/regression/issue/21257.test.ts`) - [x] Response tests pass (`test/js/web/fetch/response.test.ts`) - [x] Manual verification that output is correct for various JSON inputs Fixes oven-sh#25693 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Bot <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Sosuke Suzuki <[email protected]>
### What does this PR do? Enables `CHECK_REF_COUNTED_LIFECYCLE` in WebKit ( oven-sh/WebKit#121 ) See also WebKit/WebKit@a978fae #### `CHECK_REF_COUNTED_LIFECYCLE`? A compile-time macro that enables lifecycle validation for reference-counted objects in debug builds. **Definition** ```cpp #if ASSERT_ENABLED || ENABLE(SECURITY_ASSERTIONS) #define CHECK_REF_COUNTED_LIFECYCLE 1 #else #define CHECK_REF_COUNTED_LIFECYCLE 0 #endif ``` **Purpose** Detects three categories of bugs: 1. Missing adoption - Objects stored in RefPtr without using adoptRef() 2. Ref during destruction - ref() called while destructor is running (causes dangling pointers) 3. Thread safety violations - Unsafe ref/deref across threads **Implementation** When enabled, RefCountDebugger adds two tracking flags: - m_deletionHasBegun - Set when destructor starts - m_adoptionIsRequired - Cleared when adoptRef() is called These flags are checked on every ref()/deref() call, with assertions failing on violations. **Motivation** Refactored debug code into a separate RefCountDebugger class to: - Improve readability of core refcount logic - Eliminate duplicate code across RefCounted, ThreadSafeRefCounted, etc. - Simplify adding new refcount classes **Overhead** Zero in release builds - the flags and checks are compiled out entirely. ### How did you verify your code works? --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
…sh#25720) Closes oven-sh#8254 Fixes a data corruption bug in `Bun.write()` where files larger than 2GB would have chunks skipped resulting in corrupted output with missing data. The `doWriteLoop` had an issue where it would essentially end up offsetting twice every 2GB chunks: - it first sliced the buffer by `total_written`: ```remain = remain[@min(this.total_written, remain.len)..]``` - it would then increment `bytes_blob.offset`: `this.bytes_blob.offset += @truncate(wrote)` but because `sharedView()` already uses the blob offset `slice_ = slice_[this.offset..]` it would end up doubling the offset. In a local reproduction writing a 16GB file with each 2GB chunk filled with incrementing values `[1, 2, 3, 4, 5, 6, 7, 8]`, the buggy version produced: `[1, 3, 5, 7, …]`, skipping every other chunk. The fix is to simply remove the redundant manual offset and rely only on `total_written` to track write progress.
…on (oven-sh#25667) ## Summary Adds `Bun.$.trace` for tracing shell commands without executing them. ```js const result = $.trace`cat /tmp/file.txt > output.txt`; // { operations: [...], cwd: "...", success: true, error: null } ``` ## Test plan - [x] `bun bd test test/js/bun/shell/trace.test.ts` 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Bot <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Jarred Sumner <[email protected]>
## Summary - Add maximum decompressed message size limit to WebSocket client deflate handling - Add test coverage for decompression limits ## Test plan - Run `bun test test/js/web/websocket/websocket-permessage-deflate-edge-cases.test.ts` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Bot <[email protected]> Co-authored-by: Claude <[email protected]>
) ## Summary Apply the same optimization technique from PR oven-sh#25717 (Response.json) to other APIs that use JSON.stringify internally: - **IPC message serialization** (`ipc.zig`) - used for inter-process communication - **console.log with %j format** (`ConsoleObject.zig`) - commonly used for debugging - **PostgreSQL JSON/JSONB types** (`PostgresRequest.zig`) - database operations - **MySQL JSON type** (`MySQLTypes.zig`) - database operations - **Jest %j/%o format specifiers** (`jest.zig`) - test output formatting - **Transpiler tsconfig/macros** (`JSTranspiler.zig`) - build configuration ### Root Cause When calling `JSONStringify(globalObject, value, 0)`, the space parameter `0` becomes `jsNumber(0)`, which is NOT `undefined`. This causes JSC's FastStringifier (SIMD-optimized) to bail out: ```cpp // In WebKit's JSONObject.cpp FastStringifier::stringify() if (!space.isUndefined()) { logOutcome("space"_s); return { }; // Bail out to slow path } ``` Using `jsonStringifyFast` which passes `jsUndefined()` triggers the fast path. ### Expected Performance Improvement Based on PR oven-sh#25717 results, these changes should provide ~3x speedup for JSON serialization in the affected APIs. ## Test plan - [x] Debug build compiles successfully - [x] Basic functionality verified (IPC, console.log %j, Response.json) - [x] Existing tests pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Bot <[email protected]> Co-authored-by: Claude <[email protected]>
…sh#25731) Fixes oven-sh#25716 Adds support for a `reactFastRefresh: boolean` option in the `Bun.build` JavaScript API, matching the existing `--react-fast-refresh` CLI flag. ```ts const result = await Bun.build({ reactFastRefresh: true, entrypoints: ["src/App.tsx"], }); ``` When enabled, the bundler adds React Fast Refresh transform code (`$RefreshReg$`, `$RefreshSig$`) to the output.
) ## Summary Optimize `Buffer.indexOf` and `Buffer.includes` by replacing `std::search` with Highway SIMD-optimized functions: - **Single-byte search**: `highway_index_of_char` - SIMD-accelerated character search - **Multi-byte search**: `highway_memmem` - SIMD-accelerated substring search These Highway functions are already used throughout Bun's codebase for fast string searching (URL parsing, JS lexer, etc.) and provide significant speedups for pattern matching in large buffers. ### Changes ```cpp // Before: std::search (scalar) auto it = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end()); // After: SIMD-optimized if (valueLength == 1) { size_t result = highway_index_of_char(haystackPtr, haystackLen, valuePtr[0]); } else { void* result = highway_memmem(haystackPtr, haystackLen, valuePtr, valueLength); } ``` ## Test plan - [x] Debug build compiles successfully - [x] Basic functionality verified (`indexOf`, `includes` with single and multi-byte patterns) - [ ] Existing Buffer tests pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Bot <[email protected]> Co-authored-by: Claude <[email protected]>
…h#25743) ## Summary - Fix O(n²) performance bug in JSON mode IPC when receiving large messages that arrive in chunks - Add `JsonIncomingBuffer` wrapper that tracks newline positions to avoid re-scanning - Each byte is now scanned exactly once (on arrival or when preceding message is consumed) ## Problem When data arrives in chunks in JSON mode, `decodeIPCMessage` was calling `indexOfChar(data, '\n')` on the ENTIRE accumulated buffer every time. For a 10MB message arriving in 160 chunks of 64KB: - Chunk 1: scan 64KB - Chunk 2: scan 128KB - Chunk 3: scan 192KB - ... - Chunk 160: scan 10MB Total: ~800MB scanned for one 10MB message. ## Solution Introduced a `JsonIncomingBuffer` struct that: 1. Tracks `newline_pos: ?u32` - position of known upcoming newline (if any) 2. On `append(bytes)`: Only scans new chunk for `\n` if no position is cached 3. On `consume(bytes)`: Updates or re-scans as needed after message processing This ensures O(n) scanning instead of O(n²). ## Test plan - [x] `bun run zig:check-all` passes (all platforms compile) - [x] `bun bd test test/js/bun/spawn/spawn.ipc.test.ts` - 4 tests pass - [x] `bun bd test test/js/node/child_process/child_process_ipc.test.js` - 1 test pass - [x] `bun bd test test/js/bun/spawn/bun-ipc-inherit.test.ts` - 1 test pass - [x] `bun bd test test/js/bun/spawn/spawn.ipc.bun-node.test.ts` - 1 test pass - [x] `bun bd test test/js/bun/spawn/spawn.ipc.node-bun.test.ts` - 1 test pass - [x] `bun bd test test/js/node/child_process/child_process_ipc_large_disconnect.test.js` - 1 test pass - [x] Manual verification with `child-process-send-cb-more.js` (32KB messages) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Bot <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Jarred Sumner <[email protected]>
## Summary - Fix CMake JSON parsing error when Buildkite API returns commit messages with newlines CMake's `file(READ ...)` reads files with literal newlines, which breaks `string(JSON ...)` when the JSON contains escape sequences like `\n` in string values (e.g., commit messages from Buildkite API). Use `file(STRINGS ...)` to read line-by-line, then join with `\n` to preserve valid JSON escape sequences while avoiding literal newlines. ## Test plan - Verify CMake configure succeeds when Buildkite build has commit messages with newlines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Bot <[email protected]> Co-authored-by: Claude <[email protected]>
Summary
Test plan
Fixes #25632