### What does this PR do?
Extract NO_PROXY checking logic from getHttpProxyFor into a reusable
isNoProxy method on the env Loader. This allows both fetch() and
WebSocket to check NO_PROXY even when a proxy is explicitly provided via
the proxy option (not just via http_proxy env var).
Changes:
- env_loader.zig: Extract isNoProxy() from getHttpProxyFor()
- FetchTasklet.zig: Check isNoProxy() before using explicit proxy
- WebSocket.cpp: Check Bun__isNoProxy() before using explicit proxy
- virtual_machine_exports.zig: Export Bun__isNoProxy for C++ access
- Add NO_PROXY tests for both fetch and WebSocket proxy paths
### How did you verify your code works?
Tests
---------
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
## Summary
- Improve handling of fragmented chunk data in the HTTP parser
- Add test coverage for edge cases
## Test plan
- [x] New tests pass
- [x] Existing tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
- Fix NO_PROXY environment variable to properly respect port numbers
like Node.js and curl do
- Previously `NO_PROXY=localhost:1234` would bypass proxy for all
requests to localhost regardless of port
- Now entries with ports (e.g., `localhost:8080`) do exact host:port
matching, while entries without ports continue to use suffix matching
## Test plan
- Added tests in `test/js/bun/http/proxy.test.js` covering:
- [x] Bypass proxy when NO_PROXY matches host:port exactly
- [x] Use proxy when NO_PROXY has different port
- [x] Bypass proxy when NO_PROXY has host only (no port) - existing
behavior preserved
- [x] Handle NO_PROXY with multiple entries including port
- Verified existing proxy tests still pass
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
## Summary
- Disable HTTP keep-alive when a proxy returns a 407 (Proxy
Authentication Required) status code
- This prevents subsequent requests from trying to reuse a connection
that the proxy server has closed
- Refactored proxy tests to use `describe.concurrent` and async
`Bun.spawn` patterns
## Test plan
- [x] Added test `simultaneous proxy auth failures should not hang` that
verifies multiple concurrent requests with invalid proxy credentials
complete without hanging
- [x] Existing proxy tests pass
🤖 Generated with [Claude Code](https://claude.ai/code)
## Summary
- Fix out-of-bounds access when parsing `NO_PROXY` environment variable
with empty entries
- Empty entries (e.g., `"localhost, , example.com"`) would cause a panic
when checking if the host starts with a dot
- Skip empty entries after trimming whitespace
fixes BUN-110G
fixes BUN-128V
## Test plan
- [x] Verify `NO_PROXY="localhost, , example.com"` no longer crashes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Fix use-after-free vulnerability during socket adoption by properly
tracking reallocated sockets
- Add safety checks to prevent linking closed sockets to context lists
- Properly track socket state with new `is_closed`, `adopted`, and
`is_tls` flags
## What does this PR do?
This PR improves event loop stability by addressing potential
use-after-free issues that can occur when sockets are reallocated during
adoption (e.g., when upgrading a TCP socket to TLS).
### Key Changes
**Socket State Tracking
([internal.h](packages/bun-usockets/src/internal/internal.h))**
- Added `is_closed` flag to explicitly track when a socket has been
closed
- Added `adopted` flag to mark sockets that were reallocated during
context adoption
- Added `is_tls` flag to track TLS socket state for proper low-priority
queue handling
**Safe Socket Adoption
([context.c](packages/bun-usockets/src/context.c))**
- When `us_poll_resize()` returns a new pointer (reallocation occurred),
the old socket is now:
- Marked as closed (`is_closed = 1`)
- Added to the closed socket cleanup list
- Marked as adopted (`adopted = 1`)
- Has its `prev` pointer set to the new socket for event redirection
- Added guards to
`us_internal_socket_context_link_socket/listen_socket/connecting_socket`
to prevent linking already-closed sockets
**Event Loop Handling ([loop.c](packages/bun-usockets/src/loop.c))**
- After callbacks that can trigger socket adoption (`on_open`,
`on_writable`, `on_data`), the event loop now checks if the socket was
reallocated and redirects to the new socket
- Low-priority socket handling now properly checks `is_closed` state and
uses `is_tls` flag for correct SSL handling
**Poll Resize Safety
([epoll_kqueue.c](packages/bun-usockets/src/eventing/epoll_kqueue.c))**
- Changed `us_poll_resize()` to always allocate new memory with
`us_calloc()` instead of `us_realloc()` to ensure the old pointer
remains valid for cleanup
- Now takes `old_ext_size` parameter to correctly calculate memory sizes
- Re-enabled `us_internal_loop_update_pending_ready_polls()` call in
`us_poll_change()` to ensure pending events are properly redirected
### How did you verify your code works?
Run existing CI and existing socket upgrade tests under asan build
## Summary
- Fixes silent 401 Unauthorized errors when using proxies with long
passwords (e.g., JWT tokens > 4096 chars)
- Bun was silently dropping proxy passwords exceeding 4095 characters,
falling through to code that only encoded the username
## Changes
- Added `PercentEncoding.decodeWithFallback` which uses a 4KB stack
buffer for the common case and falls back to heap allocation only for
larger inputs
- Updated proxy auth encoding in `AsyncHTTP.zig` to use the new fallback
method
## Test plan
- [x] Added test case that verifies passwords > 4096 chars are handled
correctly
- [x] Test fails with system bun (v1.3.3), passes with this fix
- [x] All 29 proxy tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
### What does this PR do?
Fixes a bug where idle WebSocket connections would cause 100% CPU usage
on macOS and other BSD systems using kqueue.
**Root cause:** The kqueue event filter comparison was using bitwise AND
(`&`) instead of equality (`==`) when checking the filter type. Combined
with missing `EV_ONESHOT` flags on writable events, this caused the
event loop to continuously spin even when no actual I/O was pending.
**Changes:**
1. **Fixed filter comparison** in `epoll_kqueue.c`: Changed `filter &
EVFILT_READ` to `filter == EVFILT_READ` (same for `EVFILT_WRITE`). The
filter field is a value, not a bitmask.
2. **Added `EV_ONESHOT` flag** to writable events: kqueue writable
events now use one-shot mode to prevent continuous triggering.
3. **Re-arm writable events when needed**: After a one-shot writable
event fires, the code now properly updates the poll state and re-arms
the writable event if another write is still pending.
### How did you verify your code works?
Added a test that:
1. Creates a TLS WebSocket server and client
2. Sends messages then lets the connection sit idle
3. Measures CPU usage over 3 seconds
4. Fails if CPU usage exceeds 2% (expected is ~0.XX% when idle)
## Summary
- When a URL object is passed as the proxy option, or when a proxy
object lacks a "url" property, ignore it instead of throwing an error
- This fixes a regression introduced in 1.3.4 where libraries like taze
that pass URL objects as proxy values would fail
## Test plan
- Added test: "proxy as URL object should be ignored (no url property)"
- passes a URL object directly as proxy
- Updated test: "proxy object without url is ignored (regression
#25413)" - proxy object with headers but no url
- Updated test: "proxy object with null url is ignored (regression
#25413)" - proxy object where url is null
- All 29 proxy tests pass
Fixes#25413🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
## Summary
- Adds stricter validation for chunk boundaries in the HTTP chunked
transfer encoding parser
- Ensures conformance with RFC 9112 requirements for chunk formatting
- Adds additional test coverage for chunked encoding edge cases
## Test plan
- Added new tests in `test/js/bun/http/request-smuggling.test.ts`
- All existing HTTP tests pass
- `bun bd test test/js/bun/http/request-smuggling.test.ts` passes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Extends `fetch()` proxy option to accept an object format: `proxy: {
url: string, headers?: Headers }`
- Allows sending custom headers to the proxy server (useful for proxy
authentication, custom routing headers, etc.)
- Headers are sent in CONNECT requests (for HTTPS targets) and direct
proxy requests (for HTTP targets)
- User-provided `Proxy-Authorization` header overrides auto-generated
credentials from URL
## Usage
```typescript
// Old format (still works)
fetch(url, { proxy: "http://proxy.example.com:8080" });
// New object format with headers
fetch(url, {
proxy: {
url: "http://proxy.example.com:8080",
headers: {
"Proxy-Authorization": "Bearer token",
"X-Custom-Proxy-Header": "value"
}
}
});
```
## Test plan
- [x] Test proxy object with url string works same as string proxy
- [x] Test proxy object with headers sends headers to proxy (HTTP
target)
- [x] Test proxy object with headers sends headers in CONNECT request
(HTTPS target)
- [x] Test proxy object with Headers instance
- [x] Test proxy object with empty headers
- [x] Test proxy object with undefined headers
- [x] Test user-provided Proxy-Authorization overrides URL credentials
- [x] All existing proxy tests pass (25 total)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
fixes: oven-sh/bun#23717
### What does this PR do?
- Align ProxyTunnel.onClose with
[HTTPClient.onClose](https://github.com/oven-sh/bun/blob/bun-v1.3.0/src/http.zig#L223-L241):
when a tunneled HTTPS response is in-progress and either
- parsing chunked trailers (trailer-line states), or
- transfer-encoding is identity with content_length == null while in
.body,
treat EOF as end-of-message and complete the request, rather than
ECONNRESET.
- Schedule proxy deref instead of deref inside callbacks to avoid
lifetime hazards.
### How did you verify your code works?
- `test/js/bun/http/proxy.test.ts`: raw TLS origin returns
close-delimited 200 OK; verified no ECONNRESET and body delivered.
- Test suite passes under bun bd test.
## Risk/compat
- Only affects CONNECT/TLS path. Direct HTTP/HTTPS unchanged. Behavior
mirrors existing
[HTTPClient.onClose](https://github.com/oven-sh/bun/blob/bun-v1.3.0/src/http.zig#L223-L241).
## Repro (minimal)
See issue; core condition is no Content-Length and no Transfer-Encoding
(close-delimited).
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
Add a new generator for JS → Zig bindings. The bulk of the conversion is
done in C++, after which the data is transformed into an FFI-safe
representation, passed to Zig, and then finally transformed into
idiomatic Zig types.
In its current form, the new bindings generator supports:
* Signed and unsigned integers
* Floats (plus a “finite” variant that disallows NaN and infinities)
* Strings
* ArrayBuffer (accepts ArrayBuffer, TypedArray, or DataView)
* Blob
* Optional types
* Nullable types (allows null, whereas Optional only allows undefined)
* Arrays
* User-defined string enumerations
* User-defined unions (fields can optionally be named to provide a
better experience in Zig)
* Null and undefined, for use in unions (can more efficiently represent
optional/nullable unions than wrapping a union in an optional)
* User-defined dictionaries (arbitrary key-value pairs; expects a JS
object and parses it into a struct)
* Default values for dictionary members
* Alternative names for dictionary members (e.g., to support both
`serverName` and `servername` without taking up twice the space)
* Descriptive error messages
* Automatic `fromJS` functions in Zig for dictionaries
* Automatic `deinit` functions for the generated Zig types
Although this bindings generator has many features not present in
`bindgen.ts`, it does not yet implement all of `bindgen.ts`'s
functionality, so for the time being, it has been named `bindgenv2`, and
its configuration is specified in `.bindv2.ts` files. Once all
`bindgen.ts`'s functionality has been incorporated, it will be renamed.
This PR ports `SSLConfig` to use the new bindings generator; see
`SSLConfig.bindv2.ts`.
(For internal tracking: fixes STAB-1319, STAB-1322, STAB-1323,
STAB-1324)
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Alistair Smith <hi@alistair.sh>
### What does this PR do?
### How did you verify your code works?
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
# bun test
Fixes#8768, Fixes#14624, Fixes#20100, Fixes#19875, Fixes#14135,
Fixes#20980, Fixes#21830, Fixes#5738, Fixes#19758, Fixes#12782,
Fixes#5585, Fixes#9548, Might fix 5996
# New features:
## Concurrent tests
Concurrent tests allow running multiple async tests at the same time.
```ts
// concurrent.test.ts
test.concurrent("this takes a while 1", async () => {
await Bun.sleep(1000);
});
test.concurrent("this takes a while 2", async () => {
await Bun.sleep(1000);
});
test.concurrent("this takes a while 3", async () => {
await Bun.sleep(1000);
});
```
Without `.concurrent`, this test file takes 3 seconds to run because
each one has to wait for the one before it to finish before it can
start.
With `.concurrent`, this file takes 1 second because all three sleeps
can run at once.
```
$> bun-after test concurrent
concurrent.test.js:
✓ this takes a while 1 [1005.36ms]
✓ this takes a while 2 [1012.51ms]
✓ this takes a while 3 [1013.15ms]
3 pass
0 fail
Ran 3 tests across 1 file. [1081.00ms]
```
To run all tests as concurrent, pass the `--concurrent` flag when
running tests.
Limitations:
- concurrent tests cannot attribute `expect()` call counts to the test,
meaning `expect.assertions()` does not function
- concurrent tests cannot use `toMatchSnapshot`. `toMatchInlineSnapshot`
is still supported.
- `beforeAll`/`afterAll` will never be executed concurrently.
`beforeEach`/`afterEach` will.
## Chaining
Chaining multiple describe/test qualifiers is now allowed. Previously,
it would fail.
```ts
// chaining-test-qualifiers.test.ts
test.failing.each([1, 2, 3])("each %i", async i => {
throw new Error(i);
});
```
```
$> bun-after test chaining-test-qualifiers
a.test.js:
✓ each 1
✓ each 2
✓ each 3
```
# Breaking changes:
## Describe ordering
Previously, describe callbacks were called immediately. Now, they are
deferred until the outer callback has finished running. The previous
order matched Jest. The new order is similar to Vitest, but does not
match exactly.
```ts
// describe-ordering.test.ts
describe("outer", () => {
console.log("outer before");
describe("inner", () => {
console.log("inner");
});
console.log("outer after");
});
```
Before, this would print
```
$> bun-before test describe-ordering
outer before
inner
outer after
```
Now, this will print
```
$> bun-after test describe-ordering
outer before
outer after
inner
```
## Test ordering
Describes are no longer always called before tests. They are now in
order.
```ts
// test-ordering.test.ts
test("one", () => {});
describe("scope", () => {
test("two", () => {});
});
test("three", () => {});
```
Before, this would print
```
$> bun-before test test-ordering
✓ scope > two
✓ one
✓ three
```
Now, this will print
```
$> bun-after test test-ordering
✓ one
✓ scope > two
✓ three
```
## Preload hooks
Previously, beforeAll in a preload ran before the first file and
afterAll ran after the last file. Now, beforeAll will run at the start
of each file and afterAll will run at the end of each file. This
behaviour matches Jest and Vitest.
```ts
// preload.ts
beforeAll(() => console.log("preload: beforeAll"));
afterAll(() => console.log("preload: afterAll"));
```
```ts
// preload-ordering-1.test.ts
test("demonstration file 1", () => {});
```
```ts
// preload-ordering-2.test.ts
test("demonstration file 2", () => {});
```
```
$> bun-before test --preload=./preload preload-ordering
preload-ordering-1.test.ts:
preload: beforeAll
✓ demonstration file 1
preload-ordering-2.test.ts:
✓ demonstration file 2
preload: afterAll
```
```
$> bun-after test --preload=./preload preload-ordering
preload-ordering-1.test.ts:
preload: beforeAll
✓ demonstration file 1
preload: afterAll
preload-ordering-2.test.ts:
preload: beforeAll
✓ demonstration file 2
preload: afterAll
```
## Describe failures
Current behaviour is that when an error is thrown inside a describe
callback, none of the tests declared there will run. Now, describes
declared inside will also not run. The new behaviour matches the
behaviour of Jest and Vitest.
```ts
// describe-failures.test.ts
describe("erroring describe", () => {
test("this test does not run because its describe failed", () => {
expect(true).toBe(true);
});
describe("inner describe", () => {
console.log("does the inner describe callback get called?");
test("does the inner test run?", () => {
expect(true).toBe(true);
});
});
throw new Error("uh oh!");
});
```
Before, the inner describe callback would be called and the inner test
would run, although the outer test would not:
```
$> bun-before test describe-failures
describe-failures.test.ts:
does the inner describe callback get called?
# Unhandled error between tests
-------------------------------
11 | throw new Error("uh oh!");
^
error: uh oh!
-------------------------------
✓ erroring describe > inner describe > does the inner test run?
1 pass
0 fail
1 error
1 expect() calls
Ran 1 test across 1 file.
Exited with code [1]
```
Now, the inner describe callback is not called at all.
```
$> bun-after test describe-failures
describe-failures.test.ts:
# Unhandled error between tests
-------------------------------
11 | throw new Error("uh oh!");
^
error: uh oh!
-------------------------------
0 pass
0 fail
1 error
Ran 0 tests across 1 file.
Exited with code [1]
```
## Hook failures
Previously, a beforeAll failure would skip subsequent beforeAll()s, the
test, and the afterAll. Now, a beforeAll failure skips any subsequent
beforeAll()s and the test, but not the afterAll.
```js
beforeAll(() => {
throw new Error("before all: uh oh!");
});
test("my test", () => {
console.log("my test");
});
afterAll(() => console.log("after all"));
```
```
$> bun-before test hook-failures
Error: before all: uh oh!
$> bun-after test hook-failures
Error: before all: uh oh!
after all
```
Previously, an async beforeEach failure would still allow the test to
run. Now, an async beforeEach failure will prevent the test from running
```js
beforeEach(() => {
await 0;
throw "uh oh!";
});
it("the test", async () => {
console.log("does the test run?");
});
```
```
$> bun-before test async-beforeeach-failure
does the test run?
error: uh oh!
uh oh!
✗ the test
$> bun-after test async-beforeeach-failure
error: uh oh!
uh oh!
✗ the test
```
## Hook timeouts
Hooks will now time out, and can have their timeout configured in an
options parameter
```js
beforeAll(async () => {
await Bun.sleep(1000);
}, 500);
test("my test", () => {
console.log("ran my test");
});
```
```
$> bun-before test hook-timeouts
ran my test
Ran 1 test across 1 file. [1011.00ms]
$> bun-after test hook-timeouts
✗ my test [501.15ms]
^ a beforeEach/afterEach hook timed out for this test.
```
## Hook execution order
beforeAll will now execute before the tests in the scope, rather than
immediately when it is called.
```ts
describe("d1", () => {
beforeAll(() => {
console.log("<d1>");
});
test("test", () => {
console.log(" test");
});
afterAll(() => {
console.log("</d1>");
});
});
describe("d2", () => {
beforeAll(() => {
console.log("<d2>");
});
test("test", () => {
console.log(" test");
});
afterAll(() => {
console.log("</d2>");
});
});
```
```
$> bun-before test ./beforeall-ordering.test.ts
<d1>
<d2>
test
</d1>
test
</d2>
$> bun-after test ./beforeall-ordering.test.ts
<d1>
test
</d1>
<d2>
test
</d2>
```
## test inside test
test() inside test() now errors rather than silently failing. Support
for this may be added in the future.
```ts
test("outer", () => {
console.log("outer");
test("inner", () => {
console.log("inner");
});
});
```
```
$> bun-before test
outer
✓ outer [0.06ms]
1 pass
0 fail
Ran 1 test across 1 file. [8.00ms]
$> bun-after test
outer
1 | test("outer", () => {
2 | console.log("outer");
3 | test("inner", () => {
^
error: Cannot call test() inside a test. Call it inside describe() instead.
✗ outer [0.71ms]
0 pass
1 fail
```
## afterAll inside test
afterAll inside a test is no longer allowed
```ts
test("test 1", () => {
afterAll(() => console.log("afterAll"));
console.log("test 1");
});
test("test 2", () => {
console.log("test 2");
});
```
```
$> bun-before
test 1
✓ test 1 [0.05ms]
test 2
✓ test 2
afterAll
$> bun-after
error: Cannot call afterAll() inside a test. Call it inside describe() instead.
✗ test 1 [1.00ms]
test 2
✓ test 2 [0.20ms]
```
# Only inside only
Previously, an outer 'describe.only' would run all tests inside it even
if there was an inner 'test.only'. Now, only the innermost only tests
are executed.
```ts
describe.only("outer", () => {
test("one", () => console.log("should not run"));
test.only("two", () => console.log("should run"));
});
```
```
$> bun-before test
should not run
should run
$> bun-after test
should run
```
With no inner only, the outer only will still run all tests:
```ts
describe.only("outer", () => {
test("test 1", () => console.log("test 1 runs"));
test("test 2", () => console.log("test 2 runs"));
});
```
# Potential follow-up work
- [ ] for concurrent tests, display headers before console.log messages
saying which test it is for
- this will need async context or similar
- refActiveExecutionEntry should also be able to know the current test
even in test.concurrent
- [ ] `test("rerun me", () => { console.log("run one time!"); });`
`--rerun-each=3` <- this runs the first and third time but not the
second time. fix.
- [ ] should to cache the JSValue created from
DoneCallback.callAsFunction
- [ ] implement retry and rerun params for tests.
- [ ] Remove finalizer on ScopeFunctions.zig by storing the data in 3
jsvalues passed in bind rather than using a custom class. We should also
migrate off of the ClassGenerator for ScopeFunctions
- [ ] support concurrent limit, how many concurrent tests are allowed to
run at a time. ie `--concurrent-limit=25`
- [ ] flag to run tests in random order
- [ ] `test.failing` should have its own style in the same way
`test.todo` passing marks as 'todo' insetead of 'passing'. right now
it's `✓` which is confusing.
- [ ] remove all instances of bun.jsc.Jest.Jest.current
- [ ] test options should be in BunTestRoot
- [ ] we will need one global still, stored in the globalobject/vm/?.
but it should not be a Jest instance.
- [ ] consider allowing test() inside test(), as well as afterEach and
afterAll. could even allow describe() too. to do this we would switch
from indices to pointers and they would be in a linked list. they would
be allocated in memorypools for perf/locality. some special
consideration is needed for making sure repeated tests lose their
temporary items. this could also improve memory usage soomewhat.
- [ ] consider using a jsc Bound Function rather than CallbackWithArgs.
bound functions allow adding arguments and they are only one value for
GC instead of many. and this removes our unnecessary three copies.
- [ ] eliminate Strong.Safe. we should be using a C++ class instead.
- [ ] consider modifying the junit reporter to print the whole describe
tree at the end instead of trying to output as test results come in. and
move it into its own file.
- [ ] expect_call_count/expect_assertions is confusing. rename to
`expect_calls`, `assert_expect_calls`. or something.
- [ ] Should make line_no be an enum with a none option and a function
to get if line nombers are enabled
- [ ] looks like we don't need to use file_id anymore (remove
`bun.jsc.Jest.Jest.runner.?.getOrPutFile(file_path).file_id;`, store the
file path directly)
- [ ] 'dot' test reporter like vitest?
- [ ] `test.failing.if(false)` errors because it can't replace mode
'failing' with mode 'skip'. this should probably be allowed instead.
- [ ] trigger timeout termination exception for `while(true) {}`
- [ ] clean up unused callbacks. as soon as we advance to the next
execution group, we can fully clean out the previous one. sometimes
within an execution sequence we can do the same.
- clean by swapping held values with undefined
- [ ] structure cache for performance for donecallback/scopefunctions
- [ ] consider migrating CallbackWithArgs to be a bound function. the
length of the bound function can exclude the specified args.
- [ ] setting both result and maybe_skip is not ideal, maybe there
should be a function to do both at once?
- [ ] try using a linked list rather than arraylist for describe/test
children, see how it affects performance
- [ ] consider a memory pool for describescope/executionentry. test if
it improves performance.
- [ ] consider making RefDataValue methods return the reason for failure
rather than ?value. that way we can improve error messages. the reason
could be a string or it could be a defined error set
- [ ] instead of 'description orelse (unnamed)', let's have description
default to 'unnamed' and not free it if it === the global that defines
that
- [ ] Add a phase before ordering results that inherits properties to
the parents. (eg inherit only from the child and inherit has_callback
from the child. and has_callback can be on describe/test individually
rather than on base). then we won't have that happening in an init()
function (terrible!)
- [ ] this test was incidentally passing because resolves.pass() wasn't
waiting for promise
```
test("fetching with Request object - issue #1527", async () => {
const server = createServer((req, res) => {
res.end();
}).listen(0);
try {
await once(server, "listening");
const body = JSON.stringify({ foo: "bar" });
const request = new Request(`http://localhost:${server.address().port}`,
{
method: "POST",
body,
});
expect(fetch(request)).resolves.pass();
} finally {
server.closeAllConnections();
}
});
```
- [ ] the error "expect.assertions() is not supported in the describe
phase, in concurrent tests, between tests, or after test execution has
completed" is not very good. we should be able to identify which of
those it is and print the right error for the context
- [ ] consider: instead of storing weak pointers to BunTest, we can
instead give the instance an id and check that it is correct when
getting the current bun test instance from the ref
- [ ] auto_killer: add three layers of auto_killer:
- preload (includes file & test)
- file (includes test)
- test
- that way at the end of the test, we kill the test processes. at the
end of the file, we kill the file processes. at the end of all, we kill
anything remaining.
AsyncLocalStorage
- store active_id & refdatavalue. active_id is a replacement for the
above weak pointers thing. refdatavalue is for determining which test it
is. this probably fits in 2×u64
- use for auto_killer so timeouts can kill even in concurrent tests
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
Fixes a TLS corruption bug in CONNECT proxy tunneling for HTTPS uploads.
When a large request body is sent over a tunneled TLS connection, the
client could interleave direct socket writes with previously buffered
encrypted bytes, causing TLS records to be emitted out-of-order. Some
proxies/upstreams detect this as a MAC mismatch and terminate with
SSLV3_ALERT_BAD_RECORD_MAC, which surfaced to users as ECONNRESET ("The
socket connection was closed unexpectedly").
This change makes `ProxyTunnel.write` preserve strict FIFO ordering of
encrypted bytes: if any bytes are already buffered, we enqueue new bytes
instead of calling `socket.write` directly. Flushing continues
exclusively via `onWritable`, which writes the buffered stream in order.
This eliminates interleaving and restores correctness for large proxied
HTTPS POST requests.
### How did you verify your code works?
- Local reproduction using a minimal script that POSTs ~20MB over HTTPS
via an HTTP proxy (CONNECT):
- Before: frequent ECONNRESET. With detailed SSL logs, upstream sent
`SSLV3_ALERT_BAD_RECORD_MAC`.
- After: requests complete successfully. Upstream responds as expected
- Verified small bodies and non-proxied HTTPS continue to work.
- Verified no linter issues and no unrelated code changes. The edit is
isolated to `src/http/ProxyTunnel.zig` and only affects the write path
to maintain TLS record ordering.
Rationale: TLS record boundaries must be preserved; mixing buffered data
with immediate writes risks fragmenting or reordering records under
backpressure. Enqueuing while buffered guarantees FIFO semantics and
avoids record corruption.
fixes:
#17434#18490 (false fix in corresponding pr)
---------
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
### What does this PR do?
update harness.ts
### How did you verify your code works?
CI
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
This PR fixes WebSocket to correctly emit an `error` event before the
`close` event when the handshake fails (e.g., 302 redirects, non-101
status codes, missing headers).
Fixes#14338
## Problem
Previously, when a WebSocket connection failed during handshake (like
receiving a 302 redirect or connecting to a non-WebSocket server), Bun
would only emit a `close` event. This behavior differed from the WHATWG
WebSocket specification and other runtimes (browsers, Node.js with `ws`,
Deno) which emit both `error` and `close` events.
## Solution
Modified `WebSocket::didFailWithErrorCode()` in `WebSocket.cpp` to pass
`isConnectionError = true` for all handshake failure error codes,
ensuring an error event is dispatched before the close event when the
connection is in the CONNECTING state.
## Changes
- Updated error handling in `src/bun.js/bindings/webcore/WebSocket.cpp`
to emit error events for handshake failures
- Added comprehensive test coverage in
`test/regression/issue/14338.test.ts`
## Test Coverage
The test file includes:
1. **Negative test**: 302 redirect response - verifies error event is
emitted
2. **Negative test**: Non-WebSocket HTTP server - verifies error event
is emitted
3. **Positive test**: Successful WebSocket connection - verifies NO
error event is emitted
All tests pass with the fix applied.
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
### How did you verify your code works?
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
Fixes https://github.com/oven-sh/bun/issues/19198
This implements RFC 9110 Section 13.1.2 If-None-Match conditional
request support for static routes in Bun.serve().
**Key Features:**
- Automatic ETag generation for static content based on content hash
- If-None-Match header evaluation with weak entity tag comparison
- 304 Not Modified responses for cache efficiency
- Standards-compliant handling of wildcards (*), multiple ETags, and
weak ETags (W/)
- Method-specific application (GET/HEAD only) with proper 405 responses
for other methods
## Implementation Details
- ETags are generated using `bun.hash()` and formatted as strong ETags
(e.g., "abc123")
- Preserves existing ETag headers from Response objects
- Uses weak comparison semantics as defined in RFC 9110 Section 8.8.3.2
- Handles comma-separated ETag lists and malformed headers gracefully
- Only applies to GET/HEAD requests with 200 status codes
## Files Changed
- `src/bun.js/api/server/StaticRoute.zig` - Core implementation (~100
lines)
- `test/js/bun/http/serve-if-none-match.test.ts` - Comprehensive test
suite (17 tests)
## Test Results
- ✅ All 17 new If-None-Match tests pass
- ✅ All 34 existing static route tests pass (no regressions)
- ✅ Debug build compiles successfully
## Test plan
- [ ] Run existing HTTP server tests to ensure no regressions
- [ ] Test ETag generation for various content types
- [ ] Verify 304 responses reduce bandwidth in real scenarios
- [ ] Test edge cases like malformed If-None-Match headers
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
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>